You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

377 lines
15 KiB

using OpenDBDiff.Abstractions.Schema;
using OpenDBDiff.Abstractions.Schema.Model;
using System;
using System.Globalization;
using System.Text;
namespace OpenDBDiff.SqlServer.Schema.Model
{
public class Constraint : SQLServerSchemaBase
{
public enum ConstraintType
{
None = 0,
PrimaryKey = 1,
ForeignKey = 2,
Default = 3,
Unique = 4,
Check = 5
}
public Constraint(ISchemaBase parent)
: this(parent, false)
{
}
public Constraint(ISchemaBase parent, bool hasIndex)
: base(parent, ObjectType.Constraint)
{
this.Columns = new ConstraintColumns(this);
if (hasIndex)
this.Index = new Index(parent);
}
/// <summary>
/// Clona el objeto Column en una nueva instancia.
/// </summary>
public override ISchemaBase Clone(ISchemaBase parent)
{
Constraint col = new Constraint(parent);
col.Id = this.Id;
col.Name = this.Name;
col.NotForReplication = this.NotForReplication;
col.RelationalTableFullName = this.RelationalTableFullName;
col.Status = this.Status;
col.Type = this.Type;
col.WithNoCheck = this.WithNoCheck;
col.OnDeleteCascade = this.OnDeleteCascade;
col.OnUpdateCascade = this.OnUpdateCascade;
col.Owner = this.Owner;
col.Columns = this.Columns.Clone();
col.Index = (Index)this.Index?.Clone(parent);
col.IsDisabled = this.IsDisabled;
col.Definition = this.Definition;
col.Guid = this.Guid;
return col;
}
/// <summary>
/// Informacion sobre le indice asociado al Constraint.
/// </summary>
public Index Index { get; set; }
/// <summary>
/// Coleccion de columnas de la constraint.
/// </summary>
public ConstraintColumns Columns { get; set; }
/// <summary>
/// Indica si la constraint tiene asociada un indice Clustered.
/// </summary>
public Boolean HasClusteredIndex
{
get
{
if (Index != null)
return (Index.Type == Index.IndexTypeEnum.Clustered);
return false;
}
}
/// <summary>
/// Gets or sets a value indicating whether this constraint is disabled.
/// </summary>
/// <value>
/// <c>true</c> if this constraint is disabled; otherwise, <c>false</c>.
/// </value>
public Boolean IsDisabled { get; set; }
/// <summary>
/// Gets or sets the on delete cascade (only for FK).
/// </summary>
/// <value>The on delete cascade.</value>
public int OnDeleteCascade { get; set; }
/// <summary>
/// Gets or sets the on update cascade (only for FK).
/// </summary>
/// <value>The on update cascade.</value>
public int OnUpdateCascade { get; set; }
/// <summary>
/// Valor de la constraint (se usa para los Check Constraint).
/// </summary>
public string Definition { get; set; }
/// <summary>
/// Indica si la constraint va a ser usada en replicacion.
/// </summary>
public Boolean NotForReplication { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [with no check].
/// </summary>
/// <value><c>true</c> if [with no check]; otherwise, <c>false</c>.</value>
public Boolean WithNoCheck { get; set; }
/// <summary>
/// Indica el tipo de constraint (PrimaryKey, ForeignKey, Unique o Default).
/// </summary>
public ConstraintType Type { get; set; }
/// <summary>
/// ID de la tabla relacionada a la que hace referencia (solo aplica a FK)
/// </summary>
public int RelationalTableId { get; set; }
/// <summary>
/// Nombre de la tabla relacionada a la que hace referencia (solo aplica a FK)
/// </summary>
public string RelationalTableFullName { get; set; }
/// <summary>
/// Compara dos campos y devuelve true si son iguales, caso contrario, devuelve false.
/// </summary>
public static Boolean Compare(Constraint origin, Constraint destination)
{
if (destination == null) throw new ArgumentNullException("destination");
if (origin == null) throw new ArgumentNullException("origin");
if (origin.NotForReplication != destination.NotForReplication) return false;
if ((origin.RelationalTableFullName == null) && (destination.RelationalTableFullName != null)) return false;
if (origin.RelationalTableFullName != null)
if (!origin.RelationalTableFullName.Equals(destination.RelationalTableFullName, StringComparison.CurrentCultureIgnoreCase)) return false;
if ((origin.Definition == null) && (destination.Definition != null)) return false;
if (origin.Definition != null)
if ((!origin.Definition.Equals(destination.Definition)) && (!origin.Definition.Equals("(" + destination.Definition + ")"))) return false;
/*Solo si la constraint esta habilitada, se chequea el is_trusted*/
if (!destination.IsDisabled)
if (origin.WithNoCheck != destination.WithNoCheck) return false;
if (origin.OnUpdateCascade != destination.OnUpdateCascade) return false;
if (origin.OnDeleteCascade != destination.OnDeleteCascade) return false;
if (!ConstraintColumns.Compare(origin.Columns, destination.Columns)) return false;
if ((origin.Index != null) && (destination.Index != null))
return Index.Compare(origin.Index, destination.Index);
return true;
}
private string ToSQLGeneric(ConstraintType consType)
{
Database database = null;
ISchemaBase current = this;
while (database == null && current.Parent != null)
{
database = current.Parent as Database;
current = current.Parent;
}
var isAzure10 = database.Info.Version == DatabaseInfo.SQLServerVersion.SQLServerAzure10;
string typeConstraint = "";
StringBuilder sql = new StringBuilder();
if (Parent.ObjectType != ObjectType.TableType)
sql.Append("CONSTRAINT [" + Name + "] ");
else
sql.Append("\t");
if (consType == ConstraintType.PrimaryKey)
sql.Append("PRIMARY KEY");
else
sql.Append("UNIQUE");
if (Index != null)
{
sql.Append(" ");
sql.Append(Index.Type.ToString().ToUpperInvariant());
}
sql.Append("\r\n\t(\r\n");
this.Columns.Sort();
for (int j = 0; j < this.Columns.Count; j++)
{
sql.Append("\t\t[" + this.Columns[j].Name + "]");
if (this.Columns[j].Order) sql.Append(" DESC"); else sql.Append(" ASC");
if (j != this.Columns.Count - 1) sql.Append(",");
sql.AppendLine();
}
sql.Append("\t)");
if (Index != null)
{
sql.Append(" WITH (");
if (Parent.ObjectType == ObjectType.TableType)
if (Index.IgnoreDupKey) sql.Append("IGNORE_DUP_KEY = ON"); else sql.Append("IGNORE_DUP_KEY = OFF");
else
{
if (!isAzure10)
{
if (Index.IsPadded) sql.Append("PAD_INDEX = ON, "); else sql.Append("PAD_INDEX = OFF, ");
}
if (Index.IsAutoStatistics) sql.Append("STATISTICS_NORECOMPUTE = ON"); else sql.Append("STATISTICS_NORECOMPUTE = OFF");
if (Index.IgnoreDupKey) sql.Append(", IGNORE_DUP_KEY = ON"); else sql.Append(", IGNORE_DUP_KEY = OFF");
if (!isAzure10)
{
if (Index.AllowRowLocks) sql.Append(", ALLOW_ROW_LOCKS = ON"); else sql.Append(", ALLOW_ROW_LOCKS = OFF");
if (Index.AllowPageLocks) sql.Append(", ALLOW_PAGE_LOCKS = ON"); else sql.Append(", ALLOW_PAGE_LOCKS = OFF");
if (Index.FillFactor != 0) sql.Append(", FILLFACTOR = " + Index.FillFactor.ToString(CultureInfo.InvariantCulture));
}
}
sql.Append(")");
if (!isAzure10)
{
if (!String.IsNullOrEmpty(Index.FileGroup)) sql.Append(" ON [" + Index.FileGroup + "]");
}
}
return sql.ToString();
}
/// <summary>
/// Devuelve el schema de la tabla en formato SQL.
/// </summary>
public override string ToSql()
{
if (this.Type == ConstraintType.PrimaryKey)
{
return ToSQLGeneric(ConstraintType.PrimaryKey);
}
if (this.Type == ConstraintType.ForeignKey)
{
StringBuilder sql = new StringBuilder();
StringBuilder sqlReference = new StringBuilder();
int indexc = 0;
this.Columns.Sort();
sql.Append("CONSTRAINT [" + Name + "] FOREIGN KEY\r\n\t(\r\n");
foreach (ConstraintColumn column in this.Columns)
{
sql.Append("\t\t[" + column.Name + "]");
sqlReference.Append("\t\t[" + column.ColumnRelationalName + "]");
if (indexc != this.Columns.Count - 1)
{
sql.Append(",");
sqlReference.Append(",");
}
sql.AppendLine();
sqlReference.AppendLine();
indexc++;
}
sql.Append("\t)\r\n");
sql.Append("\tREFERENCES " + this.RelationalTableFullName + "\r\n\t(\r\n");
sql.Append(sqlReference + "\t)");
if (OnUpdateCascade == 1) sql.Append(" ON UPDATE CASCADE");
if (OnDeleteCascade == 1) sql.Append(" ON DELETE CASCADE");
if (OnUpdateCascade == 2) sql.Append(" ON UPDATE SET NULL");
if (OnDeleteCascade == 2) sql.Append(" ON DELETE SET NULL");
if (OnUpdateCascade == 3) sql.Append(" ON UPDATE SET DEFAULT");
if (OnDeleteCascade == 3) sql.Append(" ON DELETE SET DEFAULT");
sql.Append((NotForReplication ? " NOT FOR REPLICATION" : ""));
return sql.ToString();
}
if (this.Type == ConstraintType.Unique)
{
return ToSQLGeneric(ConstraintType.Unique);
}
if (this.Type == ConstraintType.Check)
{
string sqlcheck = "";
if (Parent.ObjectType != ObjectType.TableType)
sqlcheck = "CONSTRAINT [" + Name + "] ";
return sqlcheck + "CHECK " + (NotForReplication ? "NOT FOR REPLICATION" : "") + " (" + Definition + ")";
}
return "";
}
public override string ToSqlAdd()
{
return "ALTER TABLE " + Parent.FullName + (WithNoCheck ? " WITH NOCHECK" : "") + " ADD " + ToSql() + "\r\nGO\r\n";
}
public override string ToSqlDrop()
{
return ToSqlDrop(null);
}
public override SQLScript Create()
{
ScriptAction action = ScriptAction.AddConstraint;
if (this.Type == ConstraintType.ForeignKey)
action = ScriptAction.AddConstraintFK;
if (this.Type == ConstraintType.PrimaryKey)
action = ScriptAction.AddConstraintPK;
if (!GetWasInsertInDiffList(action))
{
SetWasInsertInDiffList(action);
return new SQLScript(this.ToSqlAdd(), ((Table)Parent).DependenciesCount, action);
}
else
return null;
}
public override SQLScript Drop()
{
ScriptAction action = ScriptAction.DropConstraint;
if (this.Type == ConstraintType.ForeignKey)
action = ScriptAction.DropConstraintFK;
if (this.Type == ConstraintType.PrimaryKey)
action = ScriptAction.DropConstraintPK;
if (!GetWasInsertInDiffList(action))
{
SetWasInsertInDiffList(action);
return new SQLScript(this.ToSqlDrop(), ((Table)Parent).DependenciesCount, action);
}
else
return null;
}
public string ToSqlDrop(string FileGroupName)
{
string sql = "ALTER TABLE " + ((Table)Parent).FullName + " DROP CONSTRAINT [" + Name + "]";
if (!String.IsNullOrEmpty(FileGroupName)) sql += " WITH (MOVE TO [" + FileGroupName + "])";
sql += "\r\nGO\r\n";
return sql;
}
public string ToSQLEnabledDisabled()
{
StringBuilder sql = new StringBuilder();
if (this.IsDisabled)
return "ALTER TABLE " + Parent.FullName + " NOCHECK CONSTRAINT [" + Name + "]\r\nGO\r\n";
else
{
return "ALTER TABLE " + Parent.FullName + " CHECK CONSTRAINT [" + Name + "]\r\nGO\r\n";
}
}
public override SQLScriptList ToSqlDiff(System.Collections.Generic.ICollection<ISchemaBase> schemas)
{
SQLScriptList list = new SQLScriptList();
if (this.Status != ObjectStatus.Original)
RootParent.ActionMessage[Parent.FullName].Add(this);
if (this.HasState(ObjectStatus.Drop))
{
if (this.Parent.Status != ObjectStatus.Rebuild)
list.Add(Drop());
}
if (this.HasState(ObjectStatus.Create))
list.Add(Create());
if (this.HasState(ObjectStatus.Alter))
{
list.Add(Drop());
list.Add(Create());
}
if (this.HasState(ObjectStatus.Disabled))
{
list.Add(this.ToSQLEnabledDisabled(), ((Table)Parent).DependenciesCount, ScriptAction.AlterConstraint);
}
/*if (this.Status == StatusEnum.ObjectStatusType.ChangeFileGroup)
{
list.Add(this.ToSQLDrop(this.Index.FileGroup), ((Table)Parent).DependenciesCount, actionDrop);
list.Add(this.ToSQLAdd(), ((Table)Parent).DependenciesCount, actionAdd);
}*/
return list;
}
}
}