mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-21 05:53:33 -07:00
added better db migration support than what Subsonic provides out of the box.
This commit is contained in:
parent
180da4c82a
commit
ce63f05512
91 changed files with 7218 additions and 48 deletions
119
Migrator.net/Migrator.Providers/ColumnPropertiesMapper.cs
Normal file
119
Migrator.net/Migrator.Providers/ColumnPropertiesMapper.cs
Normal file
|
@ -0,0 +1,119 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Migrator.Framework;
|
||||
|
||||
namespace Migrator.Providers
|
||||
{
|
||||
/// <summary>
|
||||
/// This is basically a just a helper base class
|
||||
/// per-database implementors may want to override ColumnSql
|
||||
/// </summary>
|
||||
public class ColumnPropertiesMapper
|
||||
{
|
||||
protected Dialect dialect;
|
||||
|
||||
/// <summary>The SQL type</summary>
|
||||
protected string type;
|
||||
|
||||
/// <summary>The name of the column</summary>
|
||||
protected string name;
|
||||
|
||||
/// <summary>
|
||||
/// the type of the column
|
||||
/// </summary>
|
||||
protected string columnSql;
|
||||
|
||||
/// <summary>
|
||||
/// Sql if This column is Indexed
|
||||
/// </summary>
|
||||
protected bool indexed = false;
|
||||
|
||||
/// <summary>
|
||||
/// Sql if this column has a default value
|
||||
/// </summary>
|
||||
protected object defaultVal;
|
||||
|
||||
public ColumnPropertiesMapper(Dialect dialect, string type)
|
||||
{
|
||||
this.dialect = dialect;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The sql for this column, override in database-specific implementation classes
|
||||
/// </summary>
|
||||
public virtual string ColumnSql
|
||||
{
|
||||
get { return columnSql; }
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
set { name = value; }
|
||||
}
|
||||
|
||||
public object Default
|
||||
{
|
||||
get { return defaultVal; }
|
||||
set { defaultVal = value; }
|
||||
}
|
||||
|
||||
public string QuotedName
|
||||
{
|
||||
get { return dialect.Quote(Name); }
|
||||
}
|
||||
|
||||
public string IndexSql
|
||||
{
|
||||
get
|
||||
{
|
||||
if (dialect.SupportsIndex && indexed)
|
||||
return String.Format("INDEX({0})", dialect.Quote(name));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void MapColumnProperties(Column column)
|
||||
{
|
||||
Name = column.Name;
|
||||
indexed = PropertySelected(column.ColumnProperty, ColumnProperty.Indexed);
|
||||
|
||||
List<string> vals = new List<string>();
|
||||
vals.Add(dialect.ColumnNameNeedsQuote ? QuotedName : Name);
|
||||
|
||||
vals.Add(type);
|
||||
|
||||
if (! dialect.IdentityNeedsType)
|
||||
AddValueIfSelected(column, ColumnProperty.Identity, vals);
|
||||
|
||||
AddValueIfSelected(column, ColumnProperty.Unsigned, vals);
|
||||
if (! PropertySelected(column.ColumnProperty, ColumnProperty.PrimaryKey) || dialect.NeedsNotNullForIdentity)
|
||||
AddValueIfSelected(column, ColumnProperty.NotNull, vals);
|
||||
|
||||
AddValueIfSelected(column, ColumnProperty.PrimaryKey, vals);
|
||||
|
||||
if (dialect.IdentityNeedsType)
|
||||
AddValueIfSelected(column, ColumnProperty.Identity, vals);
|
||||
|
||||
AddValueIfSelected(column, ColumnProperty.Unique, vals);
|
||||
AddValueIfSelected(column, ColumnProperty.ForeignKey, vals);
|
||||
|
||||
if (column.DefaultValue != null)
|
||||
vals.Add(dialect.Default(column.DefaultValue));
|
||||
|
||||
columnSql = String.Join(" ", vals.ToArray());
|
||||
}
|
||||
|
||||
private void AddValueIfSelected(Column column, ColumnProperty property, ICollection<string> vals)
|
||||
{
|
||||
if (PropertySelected(column.ColumnProperty, property))
|
||||
vals.Add(dialect.SqlForProperty(property));
|
||||
}
|
||||
|
||||
public static bool PropertySelected(ColumnProperty source, ColumnProperty comparison)
|
||||
{
|
||||
return (source & comparison) == comparison;
|
||||
}
|
||||
}
|
||||
}
|
181
Migrator.net/Migrator.Providers/Dialect.cs
Normal file
181
Migrator.net/Migrator.Providers/Dialect.cs
Normal file
|
@ -0,0 +1,181 @@
|
|||
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Collections.Generic;
|
||||
using Migrator.Framework;
|
||||
|
||||
namespace Migrator.Providers
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the implementations specific details for a particular database.
|
||||
/// </summary>
|
||||
public abstract class Dialect
|
||||
{
|
||||
private readonly Dictionary<ColumnProperty, string> propertyMap = new Dictionary<ColumnProperty, string>();
|
||||
private readonly TypeNames typeNames = new TypeNames();
|
||||
|
||||
protected Dialect()
|
||||
{
|
||||
RegisterProperty(ColumnProperty.Null, "NULL");
|
||||
RegisterProperty(ColumnProperty.NotNull, "NOT NULL");
|
||||
RegisterProperty(ColumnProperty.Unique, "UNIQUE");
|
||||
RegisterProperty(ColumnProperty.PrimaryKey, "PRIMARY KEY");
|
||||
}
|
||||
|
||||
public abstract Type TransformationProvider { get; }
|
||||
|
||||
public ITransformationProvider NewProviderForDialect(string connectionString)
|
||||
{
|
||||
return (ITransformationProvider) Activator.CreateInstance(TransformationProvider, this, connectionString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subclasses register a typename for the given type code and maximum
|
||||
/// column length. <c>$l</c> in the type name will be replaced by the column
|
||||
/// length (if appropriate)
|
||||
/// </summary>
|
||||
/// <param name="code">The typecode</param>
|
||||
/// <param name="capacity">Maximum length of database type</param>
|
||||
/// <param name="name">The database type name</param>
|
||||
protected void RegisterColumnType(DbType code, int capacity, string name)
|
||||
{
|
||||
typeNames.Put(code, capacity, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Suclasses register a typename for the given type code. <c>$l</c> in the
|
||||
/// typename will be replaced by the column length (if appropriate).
|
||||
/// </summary>
|
||||
/// <param name="code">The typecode</param>
|
||||
/// <param name="name">The database type name</param>
|
||||
protected void RegisterColumnType(DbType code, string name)
|
||||
{
|
||||
typeNames.Put(code, name);
|
||||
}
|
||||
|
||||
public ColumnPropertiesMapper GetColumnMapper(Column column)
|
||||
{
|
||||
string type = column.Size > 0 ? GetTypeName(column.Type, column.Size) : GetTypeName(column.Type);
|
||||
if (! IdentityNeedsType && column.IsIdentity)
|
||||
type = String.Empty;
|
||||
|
||||
return new ColumnPropertiesMapper(this, type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the name of the database type associated with the given
|
||||
/// </summary>
|
||||
/// <param name="type">The DbType</param>
|
||||
/// <returns>The database type name used by ddl.</returns>
|
||||
public virtual string GetTypeName(DbType type)
|
||||
{
|
||||
string result = typeNames.Get(type);
|
||||
if (result == null)
|
||||
{
|
||||
throw new Exception(string.Format("No default type mapping for DbType {0}", type));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the name of the database type associated with the given
|
||||
/// </summary>
|
||||
/// <param name="type">The DbType</param>
|
||||
/// <returns>The database type name used by ddl.</returns>
|
||||
/// <param name="length"></param>
|
||||
public virtual string GetTypeName(DbType type, int length)
|
||||
{
|
||||
return GetTypeName(type, length, 0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the name of the database type associated with the given
|
||||
/// </summary>
|
||||
/// <param name="type">The DbType</param>
|
||||
/// <returns>The database type name used by ddl.</returns>
|
||||
/// <param name="length"></param>
|
||||
/// <param name="precision"></param>
|
||||
/// <param name="scale"></param>
|
||||
public virtual string GetTypeName(DbType type, int length, int precision, int scale)
|
||||
{
|
||||
string resultWithLength = typeNames.Get(type, length, precision, scale);
|
||||
if (resultWithLength != null)
|
||||
return resultWithLength;
|
||||
|
||||
return GetTypeName(type);
|
||||
}
|
||||
|
||||
public void RegisterProperty(ColumnProperty property, string sql)
|
||||
{
|
||||
if (! propertyMap.ContainsKey(property))
|
||||
{
|
||||
propertyMap.Add(property, sql);
|
||||
}
|
||||
propertyMap[property] = sql;
|
||||
}
|
||||
|
||||
public string SqlForProperty(ColumnProperty property)
|
||||
{
|
||||
if (propertyMap.ContainsKey(property))
|
||||
{
|
||||
return propertyMap[property];
|
||||
}
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
public virtual bool ColumnNameNeedsQuote
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public virtual bool TableNameNeedsQuote
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public virtual bool ConstraintNameNeedsQuote
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public virtual bool IdentityNeedsType
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public virtual bool NeedsNotNullForIdentity
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public virtual bool SupportsIndex
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public virtual string Quote(string value)
|
||||
{
|
||||
return String.Format(QuoteTemplate, value);
|
||||
}
|
||||
|
||||
public virtual string QuoteTemplate
|
||||
{
|
||||
get { return "\"{0}\""; }
|
||||
}
|
||||
|
||||
public virtual string Default(object defaultValue)
|
||||
{
|
||||
return String.Format("DEFAULT {0}", defaultValue);
|
||||
}
|
||||
|
||||
public ColumnPropertiesMapper GetAndMapColumnProperties(Column column)
|
||||
{
|
||||
ColumnPropertiesMapper mapper = GetColumnMapper(column);
|
||||
mapper.MapColumnProperties(column);
|
||||
if (column.DefaultValue != null)
|
||||
mapper.Default = column.DefaultValue;
|
||||
return mapper;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using Migrator.Framework;
|
||||
|
||||
namespace Migrator.Providers
|
||||
{
|
||||
public class ForeignKeyConstraintMapper
|
||||
{
|
||||
public string SqlForConstraint(ForeignKeyConstraint constraint)
|
||||
{
|
||||
switch(constraint)
|
||||
{
|
||||
case ForeignKeyConstraint.Cascade:
|
||||
return "CASCADE";
|
||||
case ForeignKeyConstraint.Restrict:
|
||||
return "RESTRICT";
|
||||
case ForeignKeyConstraint.SetDefault:
|
||||
return "SET DEFAULT";
|
||||
case ForeignKeyConstraint.SetNull:
|
||||
return "SET NULL";
|
||||
default:
|
||||
return "NO ACTION";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
Migrator.net/Migrator.Providers/Impl/.svn/all-wcprops
Normal file
5
Migrator.net/Migrator.Providers/Impl/.svn/all-wcprops
Normal file
|
@ -0,0 +1,5 @@
|
|||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 51
|
||||
/svn/!svn/ver/144/trunk/src/Migrator.Providers/Impl
|
||||
END
|
43
Migrator.net/Migrator.Providers/Impl/.svn/entries
Normal file
43
Migrator.net/Migrator.Providers/Impl/.svn/entries
Normal file
|
@ -0,0 +1,43 @@
|
|||
10
|
||||
|
||||
dir
|
||||
147
|
||||
http://migratordotnet.googlecode.com/svn/trunk/src/Migrator.Providers/Impl
|
||||
http://migratordotnet.googlecode.com/svn
|
||||
|
||||
|
||||
|
||||
2010-03-25T22:27:09.529568Z
|
||||
144
|
||||
geofflane
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
8c3eb3c4-eb3a-0410-862c-73fa8ce6028b
|
||||
|
||||
SQLite
|
||||
dir
|
||||
|
||||
PostgreSQL
|
||||
dir
|
||||
|
||||
Oracle
|
||||
dir
|
||||
|
||||
Mysql
|
||||
dir
|
||||
|
||||
SqlServer
|
||||
dir
|
||||
|
17
Migrator.net/Migrator.Providers/Impl/SQLite/.svn/all-wcprops
Normal file
17
Migrator.net/Migrator.Providers/Impl/SQLite/.svn/all-wcprops
Normal file
|
@ -0,0 +1,17 @@
|
|||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 58
|
||||
/svn/!svn/ver/108/trunk/src/Migrator.Providers/Impl/SQLite
|
||||
END
|
||||
SQLiteTransformationProvider.cs
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 90
|
||||
/svn/!svn/ver/108/trunk/src/Migrator.Providers/Impl/SQLite/SQLiteTransformationProvider.cs
|
||||
END
|
||||
SQLiteDialect.cs
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 74
|
||||
/svn/!svn/ver/87/trunk/src/Migrator.Providers/Impl/SQLite/SQLiteDialect.cs
|
||||
END
|
96
Migrator.net/Migrator.Providers/Impl/SQLite/.svn/entries
Normal file
96
Migrator.net/Migrator.Providers/Impl/SQLite/.svn/entries
Normal file
|
@ -0,0 +1,96 @@
|
|||
10
|
||||
|
||||
dir
|
||||
147
|
||||
http://migratordotnet.googlecode.com/svn/trunk/src/Migrator.Providers/Impl/SQLite
|
||||
http://migratordotnet.googlecode.com/svn
|
||||
|
||||
|
||||
|
||||
2008-08-04T22:56:23.283456Z
|
||||
108
|
||||
geofflane
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
8c3eb3c4-eb3a-0410-862c-73fa8ce6028b
|
||||
|
||||
SQLiteTransformationProvider.cs
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2011-05-23T18:17:16.308987Z
|
||||
1a5895030f84893a6051489dcd1958d2
|
||||
2008-08-04T22:56:23.283456Z
|
||||
108
|
||||
geofflane
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
8486
|
||||
|
||||
SQLiteDialect.cs
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2011-05-23T18:17:16.309987Z
|
||||
4dc0c91dddefbb5ec3c85037f025f3f3
|
||||
2008-06-12T19:25:48.586161Z
|
||||
87
|
||||
geofflane
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1786
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
|
||||
using System;
|
||||
using System.Data;
|
||||
using Migrator.Framework;
|
||||
|
||||
namespace Migrator.Providers.SQLite
|
||||
{
|
||||
public class SQLiteDialect : Dialect
|
||||
{
|
||||
public SQLiteDialect()
|
||||
{
|
||||
RegisterColumnType(DbType.Binary, "BLOB");
|
||||
RegisterColumnType(DbType.Byte, "INTEGER");
|
||||
RegisterColumnType(DbType.Int16, "INTEGER");
|
||||
RegisterColumnType(DbType.Int32, "INTEGER");
|
||||
RegisterColumnType(DbType.Int64, "INTEGER");
|
||||
RegisterColumnType(DbType.SByte, "INTEGER");
|
||||
RegisterColumnType(DbType.UInt16, "INTEGER");
|
||||
RegisterColumnType(DbType.UInt32, "INTEGER");
|
||||
RegisterColumnType(DbType.UInt64, "INTEGER");
|
||||
RegisterColumnType(DbType.Currency, "NUMERIC");
|
||||
RegisterColumnType(DbType.Decimal, "NUMERIC");
|
||||
RegisterColumnType(DbType.Double, "NUMERIC");
|
||||
RegisterColumnType(DbType.Single, "NUMERIC");
|
||||
RegisterColumnType(DbType.VarNumeric, "NUMERIC");
|
||||
RegisterColumnType(DbType.String, "TEXT");
|
||||
RegisterColumnType(DbType.AnsiStringFixedLength, "TEXT");
|
||||
RegisterColumnType(DbType.StringFixedLength, "TEXT");
|
||||
RegisterColumnType(DbType.DateTime, "DATETIME");
|
||||
RegisterColumnType(DbType.Time, "DATETIME");
|
||||
RegisterColumnType(DbType.Boolean, "INTEGER");
|
||||
RegisterColumnType(DbType.Guid, "UNIQUEIDENTIFIER");
|
||||
|
||||
RegisterProperty(ColumnProperty.Identity, "AUTOINCREMENT");
|
||||
}
|
||||
|
||||
public override Type TransformationProvider { get { return typeof(SQLiteTransformationProvider); } }
|
||||
|
||||
public override bool NeedsNotNullForIdentity
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using Migrator.Framework;
|
||||
using ForeignKeyConstraint=Migrator.Framework.ForeignKeyConstraint;
|
||||
#if DOTNET2
|
||||
using SqliteConnection=System.Data.SQLite.SQLiteConnection;
|
||||
#else
|
||||
using Mono.Data.Sqlite;
|
||||
#endif
|
||||
|
||||
namespace Migrator.Providers.SQLite
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for SQLiteTransformationProvider.
|
||||
/// </summary>
|
||||
public class SQLiteTransformationProvider : TransformationProvider
|
||||
{
|
||||
public SQLiteTransformationProvider(Dialect dialect, string connectionString)
|
||||
: base(dialect, connectionString)
|
||||
{
|
||||
_connection = new SqliteConnection(_connectionString);
|
||||
_connection.ConnectionString = _connectionString;
|
||||
_connection.Open();
|
||||
}
|
||||
|
||||
public override void AddForeignKey(string name, string primaryTable, string[] primaryColumns, string refTable,
|
||||
string[] refColumns, ForeignKeyConstraint constraint)
|
||||
{
|
||||
// NOOP Because SQLite doesn't support foreign keys
|
||||
}
|
||||
|
||||
public override void RemoveForeignKey(string name, string table)
|
||||
{
|
||||
// NOOP Because SQLite doesn't support foreign keys
|
||||
}
|
||||
|
||||
public override void RemoveColumn(string table, string column)
|
||||
{
|
||||
if (! (TableExists(table) && ColumnExists(table, column)))
|
||||
return;
|
||||
|
||||
string[] origColDefs = GetColumnDefs(table);
|
||||
List<string> colDefs = new List<string>();
|
||||
|
||||
foreach (string origdef in origColDefs)
|
||||
{
|
||||
if (! ColumnMatch(column, origdef))
|
||||
colDefs.Add(origdef);
|
||||
}
|
||||
|
||||
string[] newColDefs = colDefs.ToArray();
|
||||
string colDefsSql = String.Join(",", newColDefs);
|
||||
|
||||
string[] colNames = ParseSqlForColumnNames(newColDefs);
|
||||
string colNamesSql = String.Join(",", colNames);
|
||||
|
||||
AddTable(table + "_temp", null, colDefsSql);
|
||||
ExecuteQuery(String.Format("INSERT INTO {0}_temp SELECT {1} FROM {0}", table, colNamesSql));
|
||||
RemoveTable(table);
|
||||
ExecuteQuery(String.Format("ALTER TABLE {0}_temp RENAME TO {0}", table));
|
||||
}
|
||||
|
||||
public override void RenameColumn(string tableName, string oldColumnName, string newColumnName)
|
||||
{
|
||||
if (ColumnExists(tableName, newColumnName))
|
||||
throw new MigrationException(String.Format("Table '{0}' has column named '{1}' already", tableName, newColumnName));
|
||||
|
||||
if (ColumnExists(tableName, oldColumnName))
|
||||
{
|
||||
string[] columnDefs = GetColumnDefs(tableName);
|
||||
string columnDef = Array.Find(columnDefs, delegate(string col) { return ColumnMatch(oldColumnName, col); });
|
||||
|
||||
string newColumnDef = columnDef.Replace(oldColumnName, newColumnName);
|
||||
|
||||
AddColumn(tableName, newColumnDef);
|
||||
ExecuteQuery(String.Format("UPDATE {0} SET {1}={2}", tableName, newColumnName, oldColumnName));
|
||||
RemoveColumn(tableName, oldColumnName);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ChangeColumn(string table, Column column)
|
||||
{
|
||||
if (! ColumnExists(table, column.Name))
|
||||
{
|
||||
Logger.Warn("Column {0}.{1} does not exist", table, column.Name);
|
||||
return;
|
||||
}
|
||||
|
||||
string tempColumn = "temp_" + column.Name;
|
||||
RenameColumn(table, column.Name, tempColumn);
|
||||
AddColumn(table, column);
|
||||
ExecuteQuery(String.Format("UPDATE {0} SET {1}={2}", table, column.Name, tempColumn));
|
||||
RemoveColumn(table, tempColumn);
|
||||
}
|
||||
|
||||
public override bool TableExists(string table)
|
||||
{
|
||||
using (IDataReader reader =
|
||||
ExecuteQuery(String.Format("SELECT name FROM sqlite_master WHERE type='table' and name='{0}'",table)))
|
||||
{
|
||||
return reader.Read();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ConstraintExists(string table, string name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override string[] GetTables()
|
||||
{
|
||||
List<string> tables = new List<string>();
|
||||
|
||||
using (IDataReader reader = ExecuteQuery("SELECT name FROM sqlite_master WHERE type='table' AND name <> 'sqlite_sequence' ORDER BY name"))
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
tables.Add((string) reader[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return tables.ToArray();
|
||||
}
|
||||
|
||||
public override Column[] GetColumns(string table)
|
||||
{
|
||||
List<Column> columns = new List<Column>();
|
||||
foreach (string columnDef in GetColumnDefs(table))
|
||||
{
|
||||
string name = ExtractNameFromColumnDef(columnDef);
|
||||
// FIXME: Need to get the real type information
|
||||
Column column = new Column(name, DbType.String);
|
||||
bool isNullable = IsNullable(columnDef);
|
||||
column.ColumnProperty |= isNullable ? ColumnProperty.Null : ColumnProperty.NotNull;
|
||||
columns.Add(column);
|
||||
}
|
||||
return columns.ToArray();
|
||||
}
|
||||
|
||||
public string GetSqlDefString(string table)
|
||||
{
|
||||
string sqldef = null;
|
||||
using (IDataReader reader = ExecuteQuery(String.Format("SELECT sql FROM sqlite_master WHERE type='table' AND name='{0}'",table)))
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
sqldef = (string) reader[0];
|
||||
}
|
||||
}
|
||||
return sqldef;
|
||||
}
|
||||
|
||||
public string[] GetColumnNames(string table)
|
||||
{
|
||||
return ParseSqlForColumnNames(GetSqlDefString(table));
|
||||
}
|
||||
|
||||
public string[] GetColumnDefs(string table)
|
||||
{
|
||||
return ParseSqlColumnDefs(GetSqlDefString(table));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Turn something like 'columnName INTEGER NOT NULL' into just 'columnName'
|
||||
/// </summary>
|
||||
public string[] ParseSqlForColumnNames(string sqldef)
|
||||
{
|
||||
string[] parts = ParseSqlColumnDefs(sqldef);
|
||||
return ParseSqlForColumnNames(parts);
|
||||
}
|
||||
|
||||
public string[] ParseSqlForColumnNames(string[] parts)
|
||||
{
|
||||
if (null == parts)
|
||||
return null;
|
||||
|
||||
for (int i = 0; i < parts.Length; i ++)
|
||||
{
|
||||
parts[i] = ExtractNameFromColumnDef(parts[i]);
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name is the first value before the space.
|
||||
/// </summary>
|
||||
/// <param name="columnDef"></param>
|
||||
/// <returns></returns>
|
||||
public string ExtractNameFromColumnDef(string columnDef)
|
||||
{
|
||||
int idx = columnDef.IndexOf(" ");
|
||||
if (idx > 0)
|
||||
{
|
||||
return columnDef.Substring(0, idx);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsNullable(string columnDef)
|
||||
{
|
||||
return ! columnDef.Contains("NOT NULL");
|
||||
}
|
||||
|
||||
public string[] ParseSqlColumnDefs(string sqldef)
|
||||
{
|
||||
if (String.IsNullOrEmpty(sqldef))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
sqldef = sqldef.Replace(Environment.NewLine, " ");
|
||||
int start = sqldef.IndexOf("(");
|
||||
int end = sqldef.IndexOf(")");
|
||||
|
||||
sqldef = sqldef.Substring(0, end);
|
||||
sqldef = sqldef.Substring(start + 1);
|
||||
|
||||
string[] cols = sqldef.Split(new char[]{','});
|
||||
for (int i = 0; i < cols.Length; i ++)
|
||||
{
|
||||
cols[i] = cols[i].Trim();
|
||||
}
|
||||
return cols;
|
||||
}
|
||||
|
||||
public bool ColumnMatch(string column, string columnDef)
|
||||
{
|
||||
return columnDef.StartsWith(column + " ") || columnDef.StartsWith(_dialect.Quote(column));
|
||||
}
|
||||
}
|
||||
}
|
44
Migrator.net/Migrator.Providers/Impl/SQLite/SQLiteDialect.cs
Normal file
44
Migrator.net/Migrator.Providers/Impl/SQLite/SQLiteDialect.cs
Normal file
|
@ -0,0 +1,44 @@
|
|||
|
||||
using System;
|
||||
using System.Data;
|
||||
using Migrator.Framework;
|
||||
|
||||
namespace Migrator.Providers.SQLite
|
||||
{
|
||||
public class SQLiteDialect : Dialect
|
||||
{
|
||||
public SQLiteDialect()
|
||||
{
|
||||
RegisterColumnType(DbType.Binary, "BLOB");
|
||||
RegisterColumnType(DbType.Byte, "INTEGER");
|
||||
RegisterColumnType(DbType.Int16, "INTEGER");
|
||||
RegisterColumnType(DbType.Int32, "INTEGER");
|
||||
RegisterColumnType(DbType.Int64, "INTEGER");
|
||||
RegisterColumnType(DbType.SByte, "INTEGER");
|
||||
RegisterColumnType(DbType.UInt16, "INTEGER");
|
||||
RegisterColumnType(DbType.UInt32, "INTEGER");
|
||||
RegisterColumnType(DbType.UInt64, "INTEGER");
|
||||
RegisterColumnType(DbType.Currency, "NUMERIC");
|
||||
RegisterColumnType(DbType.Decimal, "NUMERIC");
|
||||
RegisterColumnType(DbType.Double, "NUMERIC");
|
||||
RegisterColumnType(DbType.Single, "NUMERIC");
|
||||
RegisterColumnType(DbType.VarNumeric, "NUMERIC");
|
||||
RegisterColumnType(DbType.String, "TEXT");
|
||||
RegisterColumnType(DbType.AnsiStringFixedLength, "TEXT");
|
||||
RegisterColumnType(DbType.StringFixedLength, "TEXT");
|
||||
RegisterColumnType(DbType.DateTime, "DATETIME");
|
||||
RegisterColumnType(DbType.Time, "DATETIME");
|
||||
RegisterColumnType(DbType.Boolean, "INTEGER");
|
||||
RegisterColumnType(DbType.Guid, "UNIQUEIDENTIFIER");
|
||||
|
||||
RegisterProperty(ColumnProperty.Identity, "AUTOINCREMENT");
|
||||
}
|
||||
|
||||
public override Type TransformationProvider { get { return typeof(SQLiteTransformationProvider); } }
|
||||
|
||||
public override bool NeedsNotNullForIdentity
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using Migrator.Framework;
|
||||
using ForeignKeyConstraint=Migrator.Framework.ForeignKeyConstraint;
|
||||
#if DOTNET2
|
||||
using SqliteConnection=System.Data.SQLite.SQLiteConnection;
|
||||
#else
|
||||
using Mono.Data.Sqlite;
|
||||
#endif
|
||||
|
||||
namespace Migrator.Providers.SQLite
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for SQLiteTransformationProvider.
|
||||
/// </summary>
|
||||
public class SQLiteTransformationProvider : TransformationProvider
|
||||
{
|
||||
public SQLiteTransformationProvider(Dialect dialect, string connectionString)
|
||||
: base(dialect, connectionString)
|
||||
{
|
||||
_connection = new SqliteConnection(_connectionString);
|
||||
_connection.ConnectionString = _connectionString;
|
||||
_connection.Open();
|
||||
}
|
||||
|
||||
public override void AddForeignKey(string name, string primaryTable, string[] primaryColumns, string refTable,
|
||||
string[] refColumns, ForeignKeyConstraint constraint)
|
||||
{
|
||||
// NOOP Because SQLite doesn't support foreign keys
|
||||
}
|
||||
|
||||
public override void RemoveForeignKey(string name, string table)
|
||||
{
|
||||
// NOOP Because SQLite doesn't support foreign keys
|
||||
}
|
||||
|
||||
public override void RemoveColumn(string table, string column)
|
||||
{
|
||||
if (! (TableExists(table) && ColumnExists(table, column)))
|
||||
return;
|
||||
|
||||
string[] origColDefs = GetColumnDefs(table);
|
||||
List<string> colDefs = new List<string>();
|
||||
|
||||
foreach (string origdef in origColDefs)
|
||||
{
|
||||
if (! ColumnMatch(column, origdef))
|
||||
colDefs.Add(origdef);
|
||||
}
|
||||
|
||||
string[] newColDefs = colDefs.ToArray();
|
||||
string colDefsSql = String.Join(",", newColDefs);
|
||||
|
||||
string[] colNames = ParseSqlForColumnNames(newColDefs);
|
||||
string colNamesSql = String.Join(",", colNames);
|
||||
|
||||
AddTable(table + "_temp", null, colDefsSql);
|
||||
ExecuteQuery(String.Format("INSERT INTO {0}_temp SELECT {1} FROM {0}", table, colNamesSql));
|
||||
RemoveTable(table);
|
||||
ExecuteQuery(String.Format("ALTER TABLE {0}_temp RENAME TO {0}", table));
|
||||
}
|
||||
|
||||
public override void RenameColumn(string tableName, string oldColumnName, string newColumnName)
|
||||
{
|
||||
if (ColumnExists(tableName, newColumnName))
|
||||
throw new MigrationException(String.Format("Table '{0}' has column named '{1}' already", tableName, newColumnName));
|
||||
|
||||
if (ColumnExists(tableName, oldColumnName))
|
||||
{
|
||||
string[] columnDefs = GetColumnDefs(tableName);
|
||||
string columnDef = Array.Find(columnDefs, delegate(string col) { return ColumnMatch(oldColumnName, col); });
|
||||
|
||||
string newColumnDef = columnDef.Replace(oldColumnName, newColumnName);
|
||||
|
||||
AddColumn(tableName, newColumnDef);
|
||||
ExecuteQuery(String.Format("UPDATE {0} SET {1}={2}", tableName, newColumnName, oldColumnName));
|
||||
RemoveColumn(tableName, oldColumnName);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ChangeColumn(string table, Column column)
|
||||
{
|
||||
if (! ColumnExists(table, column.Name))
|
||||
{
|
||||
Logger.Warn("Column {0}.{1} does not exist", table, column.Name);
|
||||
return;
|
||||
}
|
||||
|
||||
string tempColumn = "temp_" + column.Name;
|
||||
RenameColumn(table, column.Name, tempColumn);
|
||||
AddColumn(table, column);
|
||||
ExecuteQuery(String.Format("UPDATE {0} SET {1}={2}", table, column.Name, tempColumn));
|
||||
RemoveColumn(table, tempColumn);
|
||||
}
|
||||
|
||||
public override bool TableExists(string table)
|
||||
{
|
||||
using (IDataReader reader =
|
||||
ExecuteQuery(String.Format("SELECT name FROM sqlite_master WHERE type='table' and name='{0}'",table)))
|
||||
{
|
||||
return reader.Read();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ConstraintExists(string table, string name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override string[] GetTables()
|
||||
{
|
||||
List<string> tables = new List<string>();
|
||||
|
||||
using (IDataReader reader = ExecuteQuery("SELECT name FROM sqlite_master WHERE type='table' AND name <> 'sqlite_sequence' ORDER BY name"))
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
tables.Add((string) reader[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return tables.ToArray();
|
||||
}
|
||||
|
||||
public override Column[] GetColumns(string table)
|
||||
{
|
||||
List<Column> columns = new List<Column>();
|
||||
foreach (string columnDef in GetColumnDefs(table))
|
||||
{
|
||||
string name = ExtractNameFromColumnDef(columnDef);
|
||||
// FIXME: Need to get the real type information
|
||||
Column column = new Column(name, DbType.String);
|
||||
bool isNullable = IsNullable(columnDef);
|
||||
column.ColumnProperty |= isNullable ? ColumnProperty.Null : ColumnProperty.NotNull;
|
||||
columns.Add(column);
|
||||
}
|
||||
return columns.ToArray();
|
||||
}
|
||||
|
||||
public string GetSqlDefString(string table)
|
||||
{
|
||||
string sqldef = null;
|
||||
using (IDataReader reader = ExecuteQuery(String.Format("SELECT sql FROM sqlite_master WHERE type='table' AND name='{0}'",table)))
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
sqldef = (string) reader[0];
|
||||
}
|
||||
}
|
||||
return sqldef;
|
||||
}
|
||||
|
||||
public string[] GetColumnNames(string table)
|
||||
{
|
||||
return ParseSqlForColumnNames(GetSqlDefString(table));
|
||||
}
|
||||
|
||||
public string[] GetColumnDefs(string table)
|
||||
{
|
||||
return ParseSqlColumnDefs(GetSqlDefString(table));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Turn something like 'columnName INTEGER NOT NULL' into just 'columnName'
|
||||
/// </summary>
|
||||
public string[] ParseSqlForColumnNames(string sqldef)
|
||||
{
|
||||
string[] parts = ParseSqlColumnDefs(sqldef);
|
||||
return ParseSqlForColumnNames(parts);
|
||||
}
|
||||
|
||||
public string[] ParseSqlForColumnNames(string[] parts)
|
||||
{
|
||||
if (null == parts)
|
||||
return null;
|
||||
|
||||
for (int i = 0; i < parts.Length; i ++)
|
||||
{
|
||||
parts[i] = ExtractNameFromColumnDef(parts[i]);
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name is the first value before the space.
|
||||
/// </summary>
|
||||
/// <param name="columnDef"></param>
|
||||
/// <returns></returns>
|
||||
public string ExtractNameFromColumnDef(string columnDef)
|
||||
{
|
||||
int idx = columnDef.IndexOf(" ");
|
||||
if (idx > 0)
|
||||
{
|
||||
return columnDef.Substring(0, idx);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsNullable(string columnDef)
|
||||
{
|
||||
return ! columnDef.Contains("NOT NULL");
|
||||
}
|
||||
|
||||
public string[] ParseSqlColumnDefs(string sqldef)
|
||||
{
|
||||
if (String.IsNullOrEmpty(sqldef))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
sqldef = sqldef.Replace(Environment.NewLine, " ");
|
||||
int start = sqldef.IndexOf("(");
|
||||
int end = sqldef.LastIndexOf(")");
|
||||
|
||||
sqldef = sqldef.Substring(0, end);
|
||||
sqldef = sqldef.Substring(start + 1);
|
||||
|
||||
string[] cols = sqldef.Split(new char[]{','});
|
||||
for (int i = 0; i < cols.Length; i ++)
|
||||
{
|
||||
cols[i] = cols[i].Trim();
|
||||
}
|
||||
return cols;
|
||||
}
|
||||
|
||||
public bool ColumnMatch(string column, string columnDef)
|
||||
{
|
||||
return columnDef.StartsWith(column + " ") || columnDef.StartsWith(_dialect.Quote(column));
|
||||
}
|
||||
}
|
||||
}
|
118
Migrator.net/Migrator.Providers/Migrator.Providers.csproj
Normal file
118
Migrator.net/Migrator.Providers/Migrator.Providers.csproj
Normal file
|
@ -0,0 +1,118 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.30729</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{D58C68E4-D789-40F7-9078-C9F587D4363C}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>Migrator.Providers</RootNamespace>
|
||||
<AssemblyName>Migrator.Providers</AssemblyName>
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<OldToolsVersion>3.5</OldToolsVersion>
|
||||
<UpgradeBackupLocation>
|
||||
</UpgradeBackupLocation>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>MigratorDotNet.snk</AssemblyOriginatorKeyFile>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG;DOTNET2</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Data.SQLite">
|
||||
<HintPath>..\..\NzbDrone.Core\Libraries\System.Data.SQLite.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ColumnPropertiesMapper.cs" />
|
||||
<Compile Include="Dialect.cs" />
|
||||
<Compile Include="ForeignKeyConstraintMapper.cs" />
|
||||
<Compile Include="NoOpTransformationProvider.cs" />
|
||||
<Compile Include="Impl\SQLite\SQLiteDialect.cs" />
|
||||
<Compile Include="Impl\SQLite\SQLiteTransformationProvider.cs" />
|
||||
<Compile Include="TransformationProvider.cs" />
|
||||
<Compile Include="TypeNames.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{B4F97281-0DBD-4835-9ED8-7DFB966E87FF}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.2.0">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 2.0 %28x86%29</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.0">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.0 %28x86%29</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="MigratorDotNet.snk" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Migrator.Framework\Migrator.Framework.csproj">
|
||||
<Project>{5270F048-E580-486C-B14C-E5B9F6E539D4}</Project>
|
||||
<Name>Migrator.Framework</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\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.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
BIN
Migrator.net/Migrator.Providers/MigratorDotNet.snk
Normal file
BIN
Migrator.net/Migrator.Providers/MigratorDotNet.snk
Normal file
Binary file not shown.
336
Migrator.net/Migrator.Providers/NoOpTransformationProvider.cs
Normal file
336
Migrator.net/Migrator.Providers/NoOpTransformationProvider.cs
Normal file
|
@ -0,0 +1,336 @@
|
|||
using System.Data;
|
||||
using Migrator.Framework;
|
||||
using ForeignKeyConstraint=Migrator.Framework.ForeignKeyConstraint;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Migrator.Providers
|
||||
{
|
||||
/// <summary>
|
||||
/// No Op (Null Object Pattern) implementation of the ITransformationProvider
|
||||
/// </summary>
|
||||
public class NoOpTransformationProvider : ITransformationProvider
|
||||
{
|
||||
|
||||
public static readonly NoOpTransformationProvider Instance = new NoOpTransformationProvider();
|
||||
|
||||
private NoOpTransformationProvider()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual ILogger Logger
|
||||
{
|
||||
get { return null; }
|
||||
set { }
|
||||
}
|
||||
|
||||
public Dialect Dialect
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public string[] GetTables()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public Column[] GetColumns(string table)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public Column GetColumnByName(string table, string column)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public void RemoveForeignKey(string table, string name)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void RemoveConstraint(string table, string name)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void AddTable(string name, params Column[] columns)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void AddTable(string name, string engine, params Column[] columns)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void RemoveTable(string name)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void RenameTable(string oldName, string newName)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void RenameColumn(string tableName, string oldColumnName, string newColumnName)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void AddColumn(string table, string sqlColumn)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void RemoveColumn(string table, string column)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public bool ColumnExists(string table, string column)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TableExists(string table)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void AddColumn(string table, string column, DbType type, int size, ColumnProperty property, object defaultValue)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void AddColumn(string table, string column, DbType type)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void AddColumn(string table, string column, DbType type, object defaultValue)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void AddColumn(string table, string column, DbType type, int size)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void AddColumn(string table, string column, DbType type, ColumnProperty property)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void AddColumn(string table, string column, DbType type, int size, ColumnProperty property)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void AddPrimaryKey(string name, string table, params string[] columns)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void GenerateForeignKey(string primaryTable, string primaryColumn, string refTable, string refColumn)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void GenerateForeignKey(string primaryTable, string[] primaryColumns, string refTable, string[] refColumns)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void GenerateForeignKey(string primaryTable, string primaryColumn, string refTable, string refColumn, ForeignKeyConstraint constraint)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void GenerateForeignKey(string primaryTable, string[] primaryColumns, string refTable,
|
||||
string[] refColumns, ForeignKeyConstraint constraint)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void AddForeignKey(string name, string primaryTable, string primaryColumn, string refTable,
|
||||
string refColumn)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void AddForeignKey(string name, string primaryTable, string[] primaryColumns, string refTable, string[] refColumns)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void AddForeignKey(string name, string primaryTable, string primaryColumn, string refTable, string refColumn, ForeignKeyConstraint constraint)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void AddForeignKey(string name, string primaryTable, string[] primaryColumns, string refTable,
|
||||
string[] refColumns, ForeignKeyConstraint constraint)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void AddUniqueConstraint(string name, string table, params string[] columns)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void AddCheckConstraint(string name, string table, string checkSql)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public bool ConstraintExists(string table, string name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ChangeColumn(string table, Column column)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
|
||||
public bool PrimaryKeyExists(string table, string name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public int ExecuteNonQuery(string sql)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public IDataReader ExecuteQuery(string sql)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ExecuteScalar(string sql)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public IDataReader Select(string what, string from)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public IDataReader Select(string what, string from, string where)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public object SelectScalar(string what, string from)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public object SelectScalar(string what, string from, string where)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public int Update(string table, string[] columns, string[] columnValues)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int Update(string table, string[] columns, string[] columnValues, string where)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int Insert(string table, string[] columns, string[] columnValues)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int Delete(string table, string[] columns, string[] columnValues)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int Delete(string table, string column, string value)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void BeginTransaction()
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void Rollback()
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void Commit()
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public ITransformationProvider this[string provider]
|
||||
{
|
||||
get { return this; }
|
||||
}
|
||||
|
||||
public void MigrationApplied(long version)
|
||||
{
|
||||
//no op
|
||||
}
|
||||
|
||||
public void MigrationUnApplied(long version)
|
||||
{
|
||||
//no op
|
||||
}
|
||||
|
||||
public List<long> AppliedMigrations
|
||||
{
|
||||
get { return new List<long>(); }
|
||||
}
|
||||
|
||||
protected void CreateSchemaInfoTable()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void AddColumn(string table, Column column)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void GenerateForeignKey(string primaryTable, string refTable)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void GenerateForeignKey(string primaryTable, string refTable, ForeignKeyConstraint constraint)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public IDbCommand GetCommand()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public void ExecuteSchemaBuilder(Migrator.Framework.SchemaBuilder.SchemaBuilder schemaBuilder)
|
||||
{
|
||||
// No Op
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
//No Op
|
||||
}
|
||||
}
|
||||
}
|
853
Migrator.net/Migrator.Providers/TransformationProvider.cs
Normal file
853
Migrator.net/Migrator.Providers/TransformationProvider.cs
Normal file
|
@ -0,0 +1,853 @@
|
|||
#region License
|
||||
|
||||
//The contents of this file are subject to the Mozilla Public License
|
||||
//Version 1.1 (the "License"); you may not use this file except in
|
||||
//compliance with the License. You may obtain a copy of the License at
|
||||
//http://www.mozilla.org/MPL/
|
||||
//Software distributed under the License is distributed on an "AS IS"
|
||||
//basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
//License for the specific language governing rights and limitations
|
||||
//under the License.
|
||||
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using Migrator.Framework;
|
||||
using Migrator.Framework.SchemaBuilder;
|
||||
using ForeignKeyConstraint = Migrator.Framework.ForeignKeyConstraint;
|
||||
using Migrator.Framework.Loggers;
|
||||
|
||||
namespace Migrator.Providers
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for every transformation providers.
|
||||
/// A 'tranformation' is an operation that modifies the database.
|
||||
/// </summary>
|
||||
public abstract class TransformationProvider : ITransformationProvider
|
||||
{
|
||||
private ILogger _logger;
|
||||
protected IDbConnection _connection;
|
||||
private IDbTransaction _transaction;
|
||||
private List<long> _appliedMigrations;
|
||||
|
||||
protected readonly string _connectionString;
|
||||
protected Dialect _dialect;
|
||||
|
||||
private readonly ForeignKeyConstraintMapper constraintMapper = new ForeignKeyConstraintMapper();
|
||||
|
||||
protected TransformationProvider(Dialect dialect, string connectionString)
|
||||
{
|
||||
_dialect = dialect;
|
||||
_connectionString = connectionString;
|
||||
_logger = new Logger(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the event logger
|
||||
/// </summary>
|
||||
public virtual ILogger Logger
|
||||
{
|
||||
get { return _logger; }
|
||||
set { _logger = value; }
|
||||
}
|
||||
|
||||
public Dialect Dialect
|
||||
{
|
||||
get { return _dialect; }
|
||||
}
|
||||
|
||||
public ITransformationProvider this[string provider]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null != provider && IsThisProvider(provider))
|
||||
return this;
|
||||
|
||||
return NoOpTransformationProvider.Instance;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsThisProvider(string provider)
|
||||
{
|
||||
// XXX: This might need to be more sophisticated. Currently just a convention
|
||||
return GetType().Name.ToLower().StartsWith(provider.ToLower());
|
||||
}
|
||||
|
||||
public virtual Column[] GetColumns(string table)
|
||||
{
|
||||
List<Column> columns = new List<Column>();
|
||||
using (
|
||||
IDataReader reader =
|
||||
ExecuteQuery(
|
||||
String.Format("select COLUMN_NAME, IS_NULLABLE from information_schema.columns where table_name = '{0}'", table)))
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
Column column = new Column(reader.GetString(0), DbType.String);
|
||||
string nullableStr = reader.GetString(1);
|
||||
bool isNullable = nullableStr == "YES";
|
||||
column.ColumnProperty |= isNullable ? ColumnProperty.Null : ColumnProperty.NotNull;
|
||||
|
||||
columns.Add(column);
|
||||
}
|
||||
}
|
||||
|
||||
return columns.ToArray();
|
||||
}
|
||||
|
||||
public virtual Column GetColumnByName(string table, string columnName)
|
||||
{
|
||||
return Array.Find(GetColumns(table),
|
||||
delegate(Column column)
|
||||
{
|
||||
return column.Name == columnName;
|
||||
});
|
||||
}
|
||||
|
||||
public virtual string[] GetTables()
|
||||
{
|
||||
List<string> tables = new List<string>();
|
||||
using (IDataReader reader = ExecuteQuery("SELECT table_name FROM information_schema.tables"))
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
tables.Add((string)reader[0]);
|
||||
}
|
||||
}
|
||||
return tables.ToArray();
|
||||
}
|
||||
|
||||
public virtual void RemoveForeignKey(string table, string name)
|
||||
{
|
||||
RemoveConstraint(table, name);
|
||||
}
|
||||
|
||||
public virtual void RemoveConstraint(string table, string name)
|
||||
{
|
||||
if (TableExists(table) && ConstraintExists(table, name))
|
||||
{
|
||||
table = _dialect.TableNameNeedsQuote ? _dialect.Quote(table) : table;
|
||||
name = _dialect.ConstraintNameNeedsQuote ? _dialect.Quote(name) : name;
|
||||
ExecuteNonQuery(String.Format("ALTER TABLE {0} DROP CONSTRAINT {1}", table, name));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void AddTable(string table, string engine, string columns)
|
||||
{
|
||||
table = _dialect.TableNameNeedsQuote ? _dialect.Quote(table) : table;
|
||||
string sqlCreate = String.Format("CREATE TABLE {0} ({1})", table, columns);
|
||||
ExecuteNonQuery(sqlCreate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a new table
|
||||
/// </summary>
|
||||
/// <param name="name">Table name</param>
|
||||
/// <param name="columns">Columns</param>
|
||||
/// <example>
|
||||
/// Adds the Test table with two columns:
|
||||
/// <code>
|
||||
/// Database.AddTable("Test",
|
||||
/// new Column("Id", typeof(int), ColumnProperty.PrimaryKey),
|
||||
/// new Column("Title", typeof(string), 100)
|
||||
/// );
|
||||
/// </code>
|
||||
/// </example>
|
||||
public virtual void AddTable(string name, params Column[] columns)
|
||||
{
|
||||
// Most databases don't have the concept of a storage engine, so default is to not use it.
|
||||
AddTable(name, null, columns);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a new table
|
||||
/// </summary>
|
||||
/// <param name="name">Table name</param>
|
||||
/// <param name="columns">Columns</param>
|
||||
/// <param name="engine">the database storage engine to use</param>
|
||||
/// <example>
|
||||
/// Adds the Test table with two columns:
|
||||
/// <code>
|
||||
/// Database.AddTable("Test", "INNODB",
|
||||
/// new Column("Id", typeof(int), ColumnProperty.PrimaryKey),
|
||||
/// new Column("Title", typeof(string), 100)
|
||||
/// );
|
||||
/// </code>
|
||||
/// </example>
|
||||
public virtual void AddTable(string name, string engine, params Column[] columns)
|
||||
{
|
||||
|
||||
if (TableExists(name))
|
||||
{
|
||||
Logger.Warn("Table {0} already exists", name);
|
||||
return;
|
||||
}
|
||||
|
||||
List<string> pks = GetPrimaryKeys(columns);
|
||||
bool compoundPrimaryKey = pks.Count > 1;
|
||||
|
||||
List<ColumnPropertiesMapper> columnProviders = new List<ColumnPropertiesMapper>(columns.Length);
|
||||
foreach (Column column in columns)
|
||||
{
|
||||
// Remove the primary key notation if compound primary key because we'll add it back later
|
||||
if (compoundPrimaryKey && column.IsPrimaryKey)
|
||||
column.ColumnProperty = ColumnProperty.Unsigned | ColumnProperty.NotNull;
|
||||
|
||||
ColumnPropertiesMapper mapper = _dialect.GetAndMapColumnProperties(column);
|
||||
columnProviders.Add(mapper);
|
||||
}
|
||||
|
||||
string columnsAndIndexes = JoinColumnsAndIndexes(columnProviders);
|
||||
AddTable(name, engine, columnsAndIndexes);
|
||||
|
||||
if (compoundPrimaryKey)
|
||||
{
|
||||
AddPrimaryKey(String.Format("PK_{0}", name), name, pks.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> GetPrimaryKeys(IEnumerable<Column> columns)
|
||||
{
|
||||
List<string> pks = new List<string>();
|
||||
foreach (Column col in columns)
|
||||
{
|
||||
if (col.IsPrimaryKey)
|
||||
pks.Add(col.Name);
|
||||
}
|
||||
return pks;
|
||||
}
|
||||
|
||||
public virtual void RemoveTable(string name)
|
||||
{
|
||||
if (TableExists(name))
|
||||
ExecuteNonQuery(String.Format("DROP TABLE {0}", name));
|
||||
}
|
||||
|
||||
public virtual void RenameTable(string oldName, string newName)
|
||||
{
|
||||
if (TableExists(newName))
|
||||
throw new MigrationException(String.Format("Table with name '{0}' already exists", newName));
|
||||
|
||||
if (TableExists(oldName))
|
||||
ExecuteNonQuery(String.Format("ALTER TABLE {0} RENAME TO {1}", oldName, newName));
|
||||
}
|
||||
|
||||
public virtual void RenameColumn(string tableName, string oldColumnName, string newColumnName)
|
||||
{
|
||||
if (ColumnExists(tableName, newColumnName))
|
||||
throw new MigrationException(String.Format("Table '{0}' has column named '{1}' already", tableName, newColumnName));
|
||||
|
||||
if (ColumnExists(tableName, oldColumnName))
|
||||
ExecuteNonQuery(String.Format("ALTER TABLE {0} RENAME COLUMN {1} TO {2}", tableName, oldColumnName, newColumnName));
|
||||
}
|
||||
|
||||
public virtual void AddColumn(string table, string sqlColumn)
|
||||
{
|
||||
ExecuteNonQuery(String.Format("ALTER TABLE {0} ADD COLUMN {1}", table, sqlColumn));
|
||||
}
|
||||
|
||||
public virtual void RemoveColumn(string table, string column)
|
||||
{
|
||||
if (ColumnExists(table, column))
|
||||
{
|
||||
ExecuteNonQuery(String.Format("ALTER TABLE {0} DROP COLUMN {1} ", table, column));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool ColumnExists(string table, string column)
|
||||
{
|
||||
try
|
||||
{
|
||||
ExecuteNonQuery(String.Format("SELECT {0} FROM {1}", column, table));
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ChangeColumn(string table, Column column)
|
||||
{
|
||||
if (!ColumnExists(table, column.Name))
|
||||
{
|
||||
Logger.Warn("Column {0}.{1} does not exist", table, column.Name);
|
||||
return;
|
||||
}
|
||||
|
||||
ColumnPropertiesMapper mapper = _dialect.GetAndMapColumnProperties(column);
|
||||
ChangeColumn(table, mapper.ColumnSql);
|
||||
}
|
||||
|
||||
public virtual void ChangeColumn(string table, string sqlColumn)
|
||||
{
|
||||
ExecuteNonQuery(String.Format("ALTER TABLE {0} ALTER COLUMN {1}", table, sqlColumn));
|
||||
}
|
||||
|
||||
public virtual bool TableExists(string table)
|
||||
{
|
||||
try
|
||||
{
|
||||
ExecuteNonQuery("SELECT COUNT(*) FROM " + table);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual string JoinColumnsAndIndexes(IEnumerable<ColumnPropertiesMapper> columns)
|
||||
{
|
||||
string indexes = JoinIndexes(columns);
|
||||
string columnsAndIndexes = JoinColumns(columns) + (indexes != null ? "," + indexes : String.Empty);
|
||||
return columnsAndIndexes;
|
||||
}
|
||||
|
||||
protected virtual string JoinIndexes(IEnumerable<ColumnPropertiesMapper> columns)
|
||||
{
|
||||
List<string> indexes = new List<string>();
|
||||
foreach (ColumnPropertiesMapper column in columns)
|
||||
{
|
||||
string indexSql = column.IndexSql;
|
||||
if (indexSql != null)
|
||||
indexes.Add(indexSql);
|
||||
}
|
||||
|
||||
if (indexes.Count == 0)
|
||||
return null;
|
||||
|
||||
return String.Join(", ", indexes.ToArray());
|
||||
}
|
||||
|
||||
protected virtual string JoinColumns(IEnumerable<ColumnPropertiesMapper> columns)
|
||||
{
|
||||
List<string> columnStrings = new List<string>();
|
||||
foreach (ColumnPropertiesMapper column in columns)
|
||||
columnStrings.Add(column.ColumnSql);
|
||||
return String.Join(", ", columnStrings.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a new column to an existing table.
|
||||
/// </summary>
|
||||
/// <param name="table">Table to which to add the column</param>
|
||||
/// <param name="column">Column name</param>
|
||||
/// <param name="type">Date type of the column</param>
|
||||
/// <param name="size">Max length of the column</param>
|
||||
/// <param name="property">Properties of the column, see <see cref="ColumnProperty">ColumnProperty</see>,</param>
|
||||
/// <param name="defaultValue">Default value</param>
|
||||
public virtual void AddColumn(string table, string column, DbType type, int size, ColumnProperty property,
|
||||
object defaultValue)
|
||||
{
|
||||
if (ColumnExists(table, column))
|
||||
{
|
||||
Logger.Warn("Column {0}.{1} already exists", table, column);
|
||||
return;
|
||||
}
|
||||
|
||||
ColumnPropertiesMapper mapper =
|
||||
_dialect.GetAndMapColumnProperties(new Column(column, type, size, property, defaultValue));
|
||||
|
||||
AddColumn(table, mapper.ColumnSql);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="TransformationProvider.AddColumn(string, string, DbType, int, ColumnProperty, object)">
|
||||
/// AddColumn(string, string, Type, int, ColumnProperty, object)
|
||||
/// </see>
|
||||
/// </summary>
|
||||
public virtual void AddColumn(string table, string column, DbType type)
|
||||
{
|
||||
AddColumn(table, column, type, 0, ColumnProperty.Null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="TransformationProvider.AddColumn(string, string, DbType, int, ColumnProperty, object)">
|
||||
/// AddColumn(string, string, Type, int, ColumnProperty, object)
|
||||
/// </see>
|
||||
/// </summary>
|
||||
public virtual void AddColumn(string table, string column, DbType type, int size)
|
||||
{
|
||||
AddColumn(table, column, type, size, ColumnProperty.Null, null);
|
||||
}
|
||||
|
||||
public void AddColumn(string table, string column, DbType type, object defaultValue)
|
||||
{
|
||||
if (ColumnExists(table, column))
|
||||
{
|
||||
Logger.Warn("Column {0}.{1} already exists", table, column);
|
||||
return;
|
||||
}
|
||||
|
||||
ColumnPropertiesMapper mapper =
|
||||
_dialect.GetAndMapColumnProperties(new Column(column, type, defaultValue));
|
||||
|
||||
AddColumn(table, mapper.ColumnSql);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="TransformationProvider.AddColumn(string, string, DbType, int, ColumnProperty, object)">
|
||||
/// AddColumn(string, string, Type, int, ColumnProperty, object)
|
||||
/// </see>
|
||||
/// </summary>
|
||||
public virtual void AddColumn(string table, string column, DbType type, ColumnProperty property)
|
||||
{
|
||||
AddColumn(table, column, type, 0, property, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="TransformationProvider.AddColumn(string, string, DbType, int, ColumnProperty, object)">
|
||||
/// AddColumn(string, string, Type, int, ColumnProperty, object)
|
||||
/// </see>
|
||||
/// </summary>
|
||||
public virtual void AddColumn(string table, string column, DbType type, int size, ColumnProperty property)
|
||||
{
|
||||
AddColumn(table, column, type, size, property, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Append a primary key to a table.
|
||||
/// </summary>
|
||||
/// <param name="name">Constraint name</param>
|
||||
/// <param name="table">Table name</param>
|
||||
/// <param name="columns">Primary column names</param>
|
||||
public virtual void AddPrimaryKey(string name, string table, params string[] columns)
|
||||
{
|
||||
if (ConstraintExists(table, name))
|
||||
{
|
||||
Logger.Warn("Primary key {0} already exists", name);
|
||||
return;
|
||||
}
|
||||
ExecuteNonQuery(
|
||||
String.Format("ALTER TABLE {0} ADD CONSTRAINT {1} PRIMARY KEY ({2}) ", table, name,
|
||||
String.Join(",", columns)));
|
||||
}
|
||||
|
||||
public virtual void AddUniqueConstraint(string name, string table, params string[] columns)
|
||||
{
|
||||
if (ConstraintExists(table, name))
|
||||
{
|
||||
Logger.Warn("Constraint {0} already exists", name);
|
||||
return;
|
||||
}
|
||||
ExecuteNonQuery(String.Format("ALTER TABLE {0} ADD CONSTRAINT {1} UNIQUE({2}) ", table, name, string.Join(", ", columns)));
|
||||
}
|
||||
|
||||
public virtual void AddCheckConstraint(string name, string table, string checkSql)
|
||||
{
|
||||
if (ConstraintExists(table, name))
|
||||
{
|
||||
Logger.Warn("Constraint {0} already exists", name);
|
||||
return;
|
||||
}
|
||||
ExecuteNonQuery(String.Format("ALTER TABLE {0} ADD CONSTRAINT {1} CHECK ({2}) ", table, name, checkSql));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Guesses the name of the foreign key and add it
|
||||
/// </summary>
|
||||
public virtual void GenerateForeignKey(string primaryTable, string primaryColumn, string refTable, string refColumn)
|
||||
{
|
||||
AddForeignKey("FK_" + primaryTable + "_" + refTable, primaryTable, primaryColumn, refTable, refColumn);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Guesses the name of the foreign key and add it
|
||||
/// </see>
|
||||
/// </summary>
|
||||
public virtual void GenerateForeignKey(string primaryTable, string[] primaryColumns, string refTable,
|
||||
string[] refColumns)
|
||||
{
|
||||
AddForeignKey("FK_" + primaryTable + "_" + refTable, primaryTable, primaryColumns, refTable, refColumns);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Guesses the name of the foreign key and add it
|
||||
/// </summary>
|
||||
public virtual void GenerateForeignKey(string primaryTable, string primaryColumn, string refTable,
|
||||
string refColumn, ForeignKeyConstraint constraint)
|
||||
{
|
||||
AddForeignKey("FK_" + primaryTable + "_" + refTable, primaryTable, primaryColumn, refTable, refColumn,
|
||||
constraint);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Guesses the name of the foreign key and add it
|
||||
/// </see>
|
||||
/// </summary>
|
||||
public virtual void GenerateForeignKey(string primaryTable, string[] primaryColumns, string refTable,
|
||||
string[] refColumns, ForeignKeyConstraint constraint)
|
||||
{
|
||||
AddForeignKey("FK_" + primaryTable + "_" + refTable, primaryTable, primaryColumns, refTable, refColumns,
|
||||
constraint);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Append a foreign key (relation) between two tables.
|
||||
/// tables.
|
||||
/// </summary>
|
||||
/// <param name="name">Constraint name</param>
|
||||
/// <param name="primaryTable">Table name containing the primary key</param>
|
||||
/// <param name="primaryColumn">Primary key column name</param>
|
||||
/// <param name="refTable">Foreign table name</param>
|
||||
/// <param name="refColumn">Foreign column name</param>
|
||||
public virtual void AddForeignKey(string name, string primaryTable, string primaryColumn, string refTable,
|
||||
string refColumn)
|
||||
{
|
||||
AddForeignKey(name, primaryTable, new string[] { primaryColumn }, refTable, new string[] { refColumn });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="ITransformationProvider.AddForeignKey(string, string, string, string, string)">
|
||||
/// AddForeignKey(string, string, string, string, string)
|
||||
/// </see>
|
||||
/// </summary>
|
||||
public virtual void AddForeignKey(string name, string primaryTable, string[] primaryColumns, string refTable, string[] refColumns)
|
||||
{
|
||||
AddForeignKey(name, primaryTable, primaryColumns, refTable, refColumns, ForeignKeyConstraint.NoAction);
|
||||
}
|
||||
|
||||
public virtual void AddForeignKey(string name, string primaryTable, string primaryColumn, string refTable, string refColumn, ForeignKeyConstraint constraint)
|
||||
{
|
||||
AddForeignKey(name, primaryTable, new string[] { primaryColumn }, refTable, new string[] { refColumn },
|
||||
constraint);
|
||||
}
|
||||
|
||||
public virtual void AddForeignKey(string name, string primaryTable, string[] primaryColumns, string refTable,
|
||||
string[] refColumns, ForeignKeyConstraint constraint)
|
||||
{
|
||||
if (ConstraintExists(primaryTable, name))
|
||||
{
|
||||
Logger.Warn("Constraint {0} already exists", name);
|
||||
return;
|
||||
}
|
||||
|
||||
string constraintResolved = constraintMapper.SqlForConstraint(constraint);
|
||||
ExecuteNonQuery(
|
||||
String.Format(
|
||||
"ALTER TABLE {0} ADD CONSTRAINT {1} FOREIGN KEY ({2}) REFERENCES {3} ({4}) ON UPDATE {5} ON DELETE {6}",
|
||||
primaryTable, name, String.Join(",", primaryColumns),
|
||||
refTable, String.Join(",", refColumns), constraintResolved, constraintResolved));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a constraint exists.
|
||||
/// </summary>
|
||||
/// <param name="name">Constraint name</param>
|
||||
/// <param name="table">Table owning the constraint</param>
|
||||
/// <returns><c>true</c> if the constraint exists.</returns>
|
||||
public abstract bool ConstraintExists(string table, string name);
|
||||
|
||||
public virtual bool PrimaryKeyExists(string table, string name)
|
||||
{
|
||||
return ConstraintExists(table, name);
|
||||
}
|
||||
|
||||
public int ExecuteNonQuery(string sql)
|
||||
{
|
||||
Logger.Trace(sql);
|
||||
Logger.ApplyingDBChange(sql);
|
||||
using (IDbCommand cmd = BuildCommand(sql))
|
||||
{
|
||||
try
|
||||
{
|
||||
return cmd.ExecuteNonQuery();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warn(ex.Message);
|
||||
throw;
|
||||
}
|
||||
}
}
|
||||
|
||||
private IDbCommand BuildCommand(string sql)
|
||||
{
|
||||
IDbCommand cmd = _connection.CreateCommand();
|
||||
cmd.CommandText = sql;
|
||||
cmd.CommandType = CommandType.Text;
|
||||
if (_transaction != null)
|
||||
{
|
||||
cmd.Transaction = _transaction;
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute an SQL query returning results.
|
||||
/// </summary>
|
||||
/// <param name="sql">The SQL command.</param>
|
||||
/// <returns>A data iterator, <see cref="System.Data.IDataReader">IDataReader</see>.</returns>
|
||||
public IDataReader ExecuteQuery(string sql)
|
||||
{
|
||||
Logger.Trace(sql);
|
||||
using (IDbCommand cmd = BuildCommand(sql))
|
||||
{
|
||||
try
|
||||
{
|
||||
return cmd.ExecuteReader();
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Warn("query failed: {0}", cmd.CommandText);
|
||||
throw;
|
||||
}
|
||||
}
}
|
||||
|
||||
public object ExecuteScalar(string sql)
|
||||
{
|
||||
Logger.Trace(sql);
|
||||
using (IDbCommand cmd = BuildCommand(sql))
|
||||
{
|
||||
try
|
||||
{
|
||||
return cmd.ExecuteScalar();
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Warn("Query failed: {0}", cmd.CommandText);
|
||||
throw;
|
||||
}
|
||||
}
}
|
||||
|
||||
public IDataReader Select(string what, string from)
|
||||
{
|
||||
return Select(what, from, "1=1");
|
||||
}
|
||||
|
||||
public virtual IDataReader Select(string what, string from, string where)
|
||||
{
|
||||
return ExecuteQuery(String.Format("SELECT {0} FROM {1} WHERE {2}", what, from, where));
|
||||
}
|
||||
|
||||
public object SelectScalar(string what, string from)
|
||||
{
|
||||
return SelectScalar(what, from, "1=1");
|
||||
}
|
||||
|
||||
public virtual object SelectScalar(string what, string from, string where)
|
||||
{
|
||||
return ExecuteScalar(String.Format("SELECT {0} FROM {1} WHERE {2}", what, from, where));
|
||||
}
|
||||
|
||||
public virtual int Update(string table, string[] columns, string[] values)
|
||||
{
|
||||
return Update(table, columns, values, null);
|
||||
}
|
||||
|
||||
public virtual int Update(string table, string[] columns, string[] values, string where)
|
||||
{
|
||||
string namesAndValues = JoinColumnsAndValues(columns, values);
|
||||
|
||||
string query = "UPDATE {0} SET {1}";
|
||||
if (!String.IsNullOrEmpty(where))
|
||||
{
|
||||
query += " WHERE " + where;
|
||||
}
|
||||
|
||||
return ExecuteNonQuery(String.Format(query, table, namesAndValues));
|
||||
}
|
||||
|
||||
public virtual int Insert(string table, string[] columns, string[] values)
|
||||
{
|
||||
return ExecuteNonQuery(String.Format("INSERT INTO {0} ({1}) VALUES ({2})", table, String.Join(", ", columns), String.Join(", ", QuoteValues(values))));
|
||||
}
|
||||
|
||||
public virtual int Delete(string table)
|
||||
{
|
||||
return Delete(table, (string[])null, (string[]) null);
|
||||
}
|
||||
|
||||
public virtual int Delete(string table, string[] columns, string[] values)
|
||||
{
|
||||
if (null == columns || null == values)
|
||||
{
|
||||
return ExecuteNonQuery(String.Format("DELETE FROM {0}", table));
|
||||
}
|
||||
else
|
||||
{
|
||||
return ExecuteNonQuery(String.Format("DELETE FROM {0} WHERE ({1})", table, JoinColumnsAndValues(columns, values)));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual int Delete(string table, string wherecolumn, string wherevalue)
|
||||
{
|
||||
return ExecuteNonQuery(String.Format("DELETE FROM {0} WHERE {1} = {2}", table, wherecolumn, QuoteValues(wherevalue)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a transaction. Called by the migration mediator.
|
||||
/// </summary>
|
||||
public void BeginTransaction()
|
||||
{
|
||||
if (_transaction == null && _connection != null)
|
||||
{
|
||||
EnsureHasConnection();
|
||||
_transaction = _connection.BeginTransaction(IsolationLevel.ReadCommitted);
|
||||
}
|
||||
}
|
||||
|
||||
protected void EnsureHasConnection()
|
||||
{
|
||||
if (_connection.State != ConnectionState.Open)
|
||||
{
|
||||
_connection.Open();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rollback the current migration. Called by the migration mediator.
|
||||
/// </summary>
|
||||
public virtual void Rollback()
|
||||
{
|
||||
if (_transaction != null && _connection != null && _connection.State == ConnectionState.Open)
|
||||
{
|
||||
try
|
||||
{
|
||||
_transaction.Rollback();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_connection.Close();
|
||||
}
|
||||
}
|
||||
_transaction = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commit the current transaction. Called by the migrations mediator.
|
||||
/// </summary>
|
||||
public void Commit()
|
||||
{
|
||||
if (_transaction != null && _connection != null && _connection.State == ConnectionState.Open)
|
||||
{
|
||||
try
|
||||
{
|
||||
_transaction.Commit();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_connection.Close();
|
||||
}
|
||||
}
|
||||
_transaction = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The list of Migrations currently applied to the database.
|
||||
/// </summary>
|
||||
public List<long> AppliedMigrations
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_appliedMigrations == null)
|
||||
{
|
||||
_appliedMigrations = new List<long>();
|
||||
CreateSchemaInfoTable();
|
||||
using(IDataReader reader = Select("version","SchemaInfo")){
|
||||
while(reader.Read()){
|
||||
_appliedMigrations.Add(Convert.ToInt64(reader.GetValue(0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return _appliedMigrations;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks a Migration version number as having been applied
|
||||
/// </summary>
|
||||
/// <param name="version">The version number of the migration that was applied</param>
|
||||
public void MigrationApplied(long version)
|
||||
{
|
||||
CreateSchemaInfoTable();
|
||||
Insert("SchemaInfo",new string[]{"version"},new string[]{version.ToString()});
|
||||
_appliedMigrations.Add(version);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks a Migration version number as having been rolled back from the database
|
||||
/// </summary>
|
||||
/// <param name="version">The version number of the migration that was removed</param>
|
||||
public void MigrationUnApplied(long version)
|
||||
{
|
||||
CreateSchemaInfoTable();
|
||||
Delete("SchemaInfo", "version", version.ToString());
|
||||
_appliedMigrations.Remove(version);
|
||||
}
|
||||
|
||||
protected void CreateSchemaInfoTable()
|
||||
{
|
||||
EnsureHasConnection();
|
||||
if (!TableExists("SchemaInfo"))
|
||||
{
|
||||
AddTable("SchemaInfo", new Column("Version", DbType.Int64, ColumnProperty.PrimaryKey));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddColumn(string table, Column column)
|
||||
{
|
||||
AddColumn(table, column.Name, column.Type, column.Size, column.ColumnProperty, column.DefaultValue);
|
||||
}
|
||||
|
||||
public void GenerateForeignKey(string primaryTable, string refTable)
|
||||
{
|
||||
GenerateForeignKey(primaryTable, refTable, ForeignKeyConstraint.NoAction);
|
||||
}
|
||||
|
||||
public void GenerateForeignKey(string primaryTable, string refTable, ForeignKeyConstraint constraint)
|
||||
{
|
||||
GenerateForeignKey(primaryTable, refTable + "Id", refTable, "Id", constraint);
|
||||
}
|
||||
|
||||
public IDbCommand GetCommand()
|
||||
{
|
||||
return BuildCommand(null);
|
||||
}
|
||||
|
||||
public void ExecuteSchemaBuilder(SchemaBuilder builder)
|
||||
{
|
||||
foreach (ISchemaBuilderExpression expr in builder.Expressions)
|
||||
expr.Create(this);
|
||||
}
|
||||
|
||||
public virtual string QuoteValues(string values)
|
||||
{
|
||||
return QuoteValues(new string[] {values})[0];
|
||||
}
|
||||
|
||||
public virtual string[] QuoteValues(string[] values)
|
||||
{
|
||||
return Array.ConvertAll<string, string>(values,
|
||||
delegate(string val) {
|
||||
if (null == val)
|
||||
return "null";
|
||||
else
|
||||
return String.Format("'{0}'", val.Replace("'", "''"));
|
||||
});
|
||||
}
|
||||
|
||||
public string JoinColumnsAndValues(string[] columns, string[] values)
|
||||
{
|
||||
string[] quotedValues = QuoteValues(values);
|
||||
string[] namesAndValues = new string[columns.Length];
|
||||
for (int i = 0; i < columns.Length; i++)
|
||||
{
|
||||
namesAndValues[i] = String.Format("{0}={1}", columns[i], quotedValues[i]);
|
||||
}
|
||||
|
||||
return String.Join(", ", namesAndValues);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_connection != null && _connection.State == ConnectionState.Open)
|
||||
{
|
||||
_connection.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
130
Migrator.net/Migrator.Providers/TypeNames.cs
Normal file
130
Migrator.net/Migrator.Providers/TypeNames.cs
Normal file
|
@ -0,0 +1,130 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using Migrator.Framework;
|
||||
|
||||
namespace Migrator.Providers
|
||||
{
|
||||
/// <summary>
|
||||
/// This class maps a DbType to names.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Associations may be marked with a capacity. Calling the <c>Get()</c>
|
||||
/// method with a type and actual size n will return the associated
|
||||
/// name with smallest capacity >= n, if available and an unmarked
|
||||
/// default type otherwise.
|
||||
/// Eg, setting
|
||||
/// <code>
|
||||
/// Names.Put(DbType, "TEXT" );
|
||||
/// Names.Put(DbType, 255, "VARCHAR($l)" );
|
||||
/// Names.Put(DbType, 65534, "LONGVARCHAR($l)" );
|
||||
/// </code>
|
||||
/// will give you back the following:
|
||||
/// <code>
|
||||
/// Names.Get(DbType) // --> "TEXT" (default)
|
||||
/// Names.Get(DbType,100) // --> "VARCHAR(100)" (100 is in [0:255])
|
||||
/// Names.Get(DbType,1000) // --> "LONGVARCHAR(1000)" (100 is in [256:65534])
|
||||
/// Names.Get(DbType,100000) // --> "TEXT" (default)
|
||||
/// </code>
|
||||
/// On the other hand, simply putting
|
||||
/// <code>
|
||||
/// Names.Put(DbType, "VARCHAR($l)" );
|
||||
/// </code>
|
||||
/// would result in
|
||||
/// <code>
|
||||
/// Names.Get(DbType) // --> "VARCHAR($l)" (will cause trouble)
|
||||
/// Names.Get(DbType,100) // --> "VARCHAR(100)"
|
||||
/// Names.Get(DbType,1000) // --> "VARCHAR(1000)"
|
||||
/// Names.Get(DbType,10000) // --> "VARCHAR(10000)"
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
public class TypeNames
|
||||
{
|
||||
public const string LengthPlaceHolder = "$l";
|
||||
public const string PrecisionPlaceHolder = "$p";
|
||||
public const string ScalePlaceHolder = "$s";
|
||||
|
||||
private readonly Dictionary<DbType, SortedList<int, string>> weighted =
|
||||
new Dictionary<DbType, SortedList<int, string>>();
|
||||
|
||||
private readonly Dictionary<DbType, string> defaults = new Dictionary<DbType, string>();
|
||||
|
||||
/// <summary>
|
||||
/// Get default type name for specified type
|
||||
/// </summary>
|
||||
/// <param name="typecode">the type key</param>
|
||||
/// <returns>the default type name associated with the specified key</returns>
|
||||
public string Get(DbType typecode)
|
||||
{
|
||||
string result;
|
||||
if (!defaults.TryGetValue(typecode, out result))
|
||||
{
|
||||
throw new ArgumentException("Dialect does not support DbType." + typecode, "typecode");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the type name specified type and size
|
||||
/// </summary>
|
||||
/// <param name="typecode">the type key</param>
|
||||
/// <param name="size">the SQL length </param>
|
||||
/// <param name="scale">the SQL scale </param>
|
||||
/// <param name="precision">the SQL precision </param>
|
||||
/// <returns>
|
||||
/// The associated name with smallest capacity >= size if available and the
|
||||
/// default type name otherwise
|
||||
/// </returns>
|
||||
public string Get(DbType typecode, int size, int precision, int scale)
|
||||
{
|
||||
SortedList<int, string> map;
|
||||
weighted.TryGetValue(typecode, out map);
|
||||
if (map != null && map.Count > 0)
|
||||
{
|
||||
foreach (KeyValuePair<int, string> entry in map)
|
||||
{
|
||||
if (size <= entry.Key)
|
||||
{
|
||||
return Replace(entry.Value, size, precision, scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
//Could not find a specific type for the size, using the default
|
||||
return Replace(Get(typecode), size, precision, scale);
|
||||
}
|
||||
|
||||
private static string Replace(string type, int size, int precision, int scale)
|
||||
{
|
||||
type = StringUtils.ReplaceOnce(type, LengthPlaceHolder, size.ToString());
|
||||
type = StringUtils.ReplaceOnce(type, ScalePlaceHolder, scale.ToString());
|
||||
return StringUtils.ReplaceOnce(type, PrecisionPlaceHolder, precision.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a type name for specified type key and capacity
|
||||
/// </summary>
|
||||
/// <param name="typecode">the type key</param>
|
||||
/// <param name="capacity">the (maximum) type size/length</param>
|
||||
/// <param name="value">The associated name</param>
|
||||
public void Put(DbType typecode, int capacity, string value)
|
||||
{
|
||||
SortedList<int, string> map;
|
||||
if (!weighted.TryGetValue(typecode, out map))
|
||||
{
|
||||
// add new ordered map
|
||||
weighted[typecode] = map = new SortedList<int, string>();
|
||||
}
|
||||
map[capacity] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="typecode"></param>
|
||||
/// <param name="value"></param>
|
||||
public void Put(DbType typecode, string value)
|
||||
{
|
||||
defaults[typecode] = value;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue