using System; using System.Collections.Generic; using System.Globalization; using System.Xml.Serialization; using OpenDBDiff.Schema.Model; namespace OpenDBDiff.Schema.SQLServer.Generates.Model { public class Column : SQLServerSchemaBase, IComparable { public Column(ISchemaBase parent) : base(parent, ObjectType.Column) { ComputedFormula = ""; Collation = ""; this.Default = new Default(this); this.Rule = new Rule(this); this.DefaultConstraint = null; } /// /// Clona el objeto Column en una nueva instancia. /// public new Column Clone(ISchemaBase parent) { Column col; if (parent == null) col = new Column(this.Parent); else col = new Column(parent); col.ComputedFormula = this.ComputedFormula; col.DataUserTypeId = this.DataUserTypeId; col.Id = this.Id; col.Guid = this.Guid; col.Owner = this.Owner; col.IdentityIncrement = this.IdentityIncrement; col.IdentitySeed = this.IdentitySeed; col.IsIdentity = this.IsIdentity; col.IsIdentityForReplication = this.IsIdentityForReplication; col.IsComputed = this.IsComputed; col.IsRowGuid = this.IsRowGuid; col.IsPersisted = this.IsPersisted; col.IsFileStream = this.IsFileStream; col.IsSparse = this.IsSparse; col.IsXmlDocument = this.IsXmlDocument; col.IsUserDefinedType = this.IsUserDefinedType; col.HasComputedDependencies = this.HasComputedDependencies; col.HasIndexDependencies = this.HasIndexDependencies; col.Name = this.Name; col.IsNullable = this.IsNullable; col.Position = this.Position; col.Precision = this.Precision; col.Scale = this.Scale; col.Collation = this.Collation; col.Size = this.Size; col.Status = this.Status; col.Type = this.Type; col.XmlSchema = this.XmlSchema; col.Default = this.Default.Clone(this); col.Rule = this.Rule.Clone(this); if (this.DefaultConstraint != null) col.DefaultConstraint = this.DefaultConstraint.Clone(this); return col; } public ColumnConstraint DefaultConstraint { get; set; } public Rule Rule { get; set; } public Default Default { get; set; } public Boolean IsFileStream { get; set; } /// /// Gets or sets a value indicating whether this instance is XML document. /// /// /// true if this instance is XML document; otherwise, false. /// public Boolean IsXmlDocument { get; set; } /// /// Gets or sets the XML schema. /// /// The XML schema. public string XmlSchema { get; set; } public Boolean IsSparse { get; set; } /// /// Gets or sets a value indicating whether this instance is user defined type. /// /// /// true if this instance is user defined type; otherwise, false. /// public Boolean IsUserDefinedType { get; set; } public int DataUserTypeId { get; set; } /// /// Gets or sets the column position. /// /// The position. public int Position { get; set; } /// /// Gets or sets the scale (only in numeric or decimal datatypes). /// /// The scale. public int Scale { get; set; } /// /// Gets or sets the precision (only in numeric or decimal datatypes). /// /// The precision. public int Precision { get; set; } /// /// Gets or sets the collation (only in text datatypes). /// /// The collation. public string Collation { get; set; } /// /// Gets or sets a value indicating whether this is nullable. /// /// true if nullable; otherwise, false. public Boolean IsNullable { get; set; } /// /// Gets or sets the size. /// /// The size. public int Size { get; set; } /// /// Gets or sets the type. /// /// The type. public string Type { get; set; } /// /// Gets or sets a value indicating whether this instance is persisted (only in Computed columns). /// /// /// true if this instance is persisted; otherwise, false. /// public Boolean IsPersisted { get; set; } /// /// Gets or sets a value indicating whether this instance has index dependencies. /// /// /// true if this instance has index dependencies; otherwise, false. /// public Boolean HasIndexDependencies { get; set; } /// /// Gets or sets a value indicating whether this instance has computed dependencies. /// /// /// true if this instance has computed dependencies; otherwise, false. /// public Boolean HasComputedDependencies { get; set; } /// /// Gets a value indicating whether this instance has to rebuild only constraint. /// /// /// true if this instance has to rebuild only constraint; otherwise, false. /// public Boolean HasToRebuildOnlyConstraint { get { return (HasIndexDependencies && !HasComputedDependencies && !IsComputed); } } /// /// Gets a value indicating whether this instance has to rebuild. /// /// /// true if this instance has to rebuild; otherwise, false. /// public Boolean HasToRebuild(int newPosition, string newType, bool isFileStream) { if (newType.Equals("text") && (!this.IsText)) return true; if (newType.Equals("ntext") && (!this.IsText)) return true; if (newType.Equals("image") && (!this.IsBinary)) return true; if (isFileStream != this.IsFileStream) return true; return ((Position != newPosition) || HasComputedDependencies || HasIndexDependencies || IsComputed || Type.ToLower().Equals("timestamp")); } /// /// Gets or sets the computed formula (only in Computed columns). /// /// The computed formula. public string ComputedFormula { get; set; } /// /// Gets or sets a value indicating whether this instance is computed. /// /// /// true if this instance is computed; otherwise, false. /// public Boolean IsComputed { get; set; } /// /// Gets a value indicating whether this column is BLOB. /// /// true if this column is BLOB; otherwise, false. public Boolean IsBLOB { get { return Type.Equals("varchar(MAX)") || Type.Equals("nvarchar(MAX)") || Type.Equals("varbinary(MAX)") || Type.Equals("text") || Type.Equals("image") || Type.Equals("ntext") || Type.Equals("xml"); } } public Boolean IsText { get { return Type.Equals("varchar(MAX)") || Type.Equals("nvarchar(MAX)") || Type.Equals("ntext") || Type.Equals("text") || Type.Equals("nvarchar") || Type.Equals("varchar") || Type.Equals("xml") || Type.Equals("char") || Type.Equals("nchar"); } } public Boolean IsBinary { get { return Type.Equals("varbinary") || Type.Equals("varbinary(MAX)") || Type.Equals("image") || Type.Equals("binary"); } } /// /// Gets or sets a value indicating whether this field is identity for replication. /// /// /// true if this field is identity for replication; otherwise, false. /// public Boolean IsIdentityForReplication { get; set; } /// /// Gets or sets a value indicating whether this field is identity. /// /// /// true if this field is identity; otherwise, false. /// public Boolean IsIdentity { get; set; } /// /// Gets or sets the identity increment (only if the field is Identity). /// /// The identity increment. public int IdentityIncrement { get; set; } /// /// Gets or sets the identity seed (only if the field is Identity). /// /// The identity seed. public long IdentitySeed { get; set; } /// /// Indica si el campo es Row Guid /// public Boolean IsRowGuid { get; set; } /// /// Nombre completo del objeto, incluyendo el owner. /// public override string FullName { get { return Parent.FullName + ".[" + Name + "]"; } } /// /// Convierte el schema de la tabla en XML. /// public string ToXML() { /*string xml = ""; xml += "\n"; xml += "" + type + ""; xml += "" + OriginalType + ""; xml += "" + size.ToString() + ""; xml += "" + (nullable ? "1":"0") + ""; xml += "" + precision.ToString() + ""; xml += "" + scale.ToString() + ""; if (this.identity) xml += ""; if (this.identityForReplication) xml += ""; xml += constraints.ToXML(); xml += "\n"; return xml;*/ XmlSerializer serial = new XmlSerializer(this.GetType()); return serial.ToString(); } public Boolean HasToForceValue { get { return (this.HasState(ObjectStatus.Update)) || ((!this.IsNullable) && (this.Status == ObjectStatus.Create)); } } /// /// Gets the default force value. /// /// The default force value. public string DefaultForceValue { get { string tl = this.Type; if (this.IsUserDefinedType) tl = ((Database)this.Parent.Parent).UserTypes[Type].Type.ToLower(); if ((((Database)Parent.Parent).Options.Defaults.UseDefaultValueIfExists) && (this.DefaultConstraint != null)) { return this.DefaultConstraint.Definition; } else { if (tl.Equals("time")) return ((Database)Parent.Parent).Options.Defaults.DefaultTime; if (tl.Equals("int") || tl.Equals("bit") || tl.Equals("smallint") || tl.Equals("bigint") || tl.Equals("tinyint")) return ((Database)Parent.Parent).Options.Defaults.DefaultIntegerValue; if (tl.Equals("text") || tl.Equals("char") || tl.Equals("varchar") || tl.Equals("varchar(max)")) return ((Database)Parent.Parent).Options.Defaults.DefaultTextValue; if (tl.Equals("ntext") || tl.Equals("nchar") || tl.Equals("nvarchar") || tl.Equals("nvarchar(max)")) return ((Database)Parent.Parent).Options.Defaults.DefaultNTextValue; if (tl.Equals("date") || tl.Equals("datetimeoffset") || tl.Equals("datetime2") || tl.Equals("datetime") || tl.Equals("smalldatetime")) return ((Database)Parent.Parent).Options.Defaults.DefaultDateValue; if (tl.Equals("numeric") || tl.Equals("decimal") || tl.Equals("float") || tl.Equals("money") || tl.Equals("smallmoney") || tl.Equals("real")) return ((Database)Parent.Parent).Options.Defaults.DefaultRealValue; if (tl.Equals("sql_variant")) return ((Database)Parent.Parent).Options.Defaults.DefaultVariantValue; if (tl.Equals("uniqueidentifier")) return ((Database)Parent.Parent).Options.Defaults.DefaultUniqueValue; if (tl.Equals("image") || tl.Equals("binary") || tl.Equals("varbinary")) return ((Database)Parent.Parent).Options.Defaults.DefaultBlobValue; } return ""; } } /// /// Toes the SQL drop. /// /// public override string ToSqlDrop() { string sql = "ALTER TABLE " + Parent.FullName + " DROP COLUMN [" + Name + "]\r\nGO\r\n"; return sql; } /// /// Toes the SQL add. /// /// public override string ToSqlAdd() { return "ALTER TABLE " + Parent.FullName + " ADD " + ToSql(false) + "\r\nGO\r\n"; } public override string ToSql() { return ToSql(true); } public string ToSQLRedefine(string type, int size, string xmlSchema) { string originalType = ""; int originalSize = 0; string originalXMLSchema = ""; string sql; if (type != null) { originalType = this.Type; this.Type = type; } if (size != 0) { originalSize = this.Size; this.Size = size; } if (xmlSchema != null) { originalXMLSchema = this.XmlSchema; this.XmlSchema = xmlSchema; } sql = this.ToSql(false); if (type != null) this.Type = originalType; if (size != 0) this.Size = originalSize; if (xmlSchema != null) this.XmlSchema = originalXMLSchema; return sql; } /// /// Devuelve el schema de la columna en formato SQL. /// public string ToSql(Boolean sqlConstraint) { string sql = ""; sql += "[" + Name + "] "; if (!IsComputed) { if (this.IsUserDefinedType) sql += Type; else sql += "[" + Type + "]"; if (Type.Equals("binary") || Type.Equals("varbinary") || Type.Equals("varchar") || Type.Equals("char") || Type.Equals("nchar") || Type.Equals("nvarchar")) { if (Size == -1) sql += " (max)"; else { if (Type.Equals("nchar") || Type.Equals("nvarchar")) sql += " (" + (Size / 2).ToString(CultureInfo.InvariantCulture) + ")"; else sql += " (" + Size.ToString(CultureInfo.InvariantCulture) + ")"; } } if (Type.Equals("xml")) { if (!String.IsNullOrEmpty(XmlSchema)) { if (IsXmlDocument) sql += "(DOCUMENT " + XmlSchema + ")"; else sql += "(CONTENT " + XmlSchema + ")"; } } if (Type.Equals("numeric") || Type.Equals("decimal")) sql += " (" + Precision.ToString(CultureInfo.InvariantCulture) + "," + Scale.ToString(CultureInfo.InvariantCulture) + ")"; if (((Database)Parent.Parent).Info.Version >= DatabaseInfo.SQLServerVersion.SQLServer2008) { if (Type.Equals("datetime2") || Type.Equals("datetimeoffset") || Type.Equals("time")) sql += "(" + Scale.ToString(CultureInfo.InvariantCulture) + ")"; } if ((!String.IsNullOrEmpty(Collation)) && (!IsUserDefinedType)) sql += " COLLATE " + Collation; if (IsIdentity) sql += " IDENTITY (" + IdentitySeed.ToString(CultureInfo.InvariantCulture) + "," + IdentityIncrement.ToString(CultureInfo.InvariantCulture) + ")"; if (IsIdentityForReplication) sql += " NOT FOR REPLICATION"; if (IsSparse) sql += " SPARSE"; if (IsFileStream) sql += " FILESTREAM"; if (IsNullable) sql += " NULL"; else sql += " NOT NULL"; if (IsRowGuid) sql += " ROWGUIDCOL"; } else { sql += "AS " + ComputedFormula; if (IsPersisted) sql += " PERSISTED"; } if ((sqlConstraint) && (DefaultConstraint != null)) { if (DefaultConstraint.Status != ObjectStatus.Drop) sql += " " + DefaultConstraint.ToSql().Replace("\t", "").Trim(); } return sql; } public SQLScriptList RebuildDependencies() { SQLScriptList list = new SQLScriptList(); list.AddRange(RebuildConstraint()); list.AddRange(RebuildIndex()); list.AddRange(RebuildFullTextIndex()); return list; } private SQLScriptList RebuildFullTextIndex() { return RebuildFullTextIndex(null); } private SQLScriptList RebuildFullTextIndex(string index) { bool it; SQLScriptList list = new SQLScriptList(); ((Table)Parent).FullTextIndex.ForEach(item => { if (index == null) it = item.Columns.Exists(col => { return col.ColumnName.Equals(this.Name); }); else it = item.Index.Equals(index); if (it) { if (item.Status != ObjectStatus.Create) list.Add(item.Drop()); if (item.Status != ObjectStatus.Drop) list.Add(item.Create()); } } ); return list; } private SQLScriptList RebuildConstraint() { SQLScriptList list = new SQLScriptList(); ((Table)Parent).Constraints.ForEach(item => { ConstraintColumn ic = item.Columns.Find(this.Id); if (ic != null) { if (item.Status != ObjectStatus.Create) list.Add(item.Drop()); if (item.Status != ObjectStatus.Drop) list.Add(item.Create()); list.AddRange(RebuildFullTextIndex(item.Name)); } }); return list; } private SQLScriptList RebuildIndex() { SQLScriptList list = new SQLScriptList(); if (HasIndexDependencies) { ((Table)Parent).Indexes.ForEach(item => { IndexColumn ic = item.Columns.Find(this.Id); if (ic != null) { if (item.Status != ObjectStatus.Create) list.Add(item.Drop()); if (item.Status != ObjectStatus.Drop) list.Add(item.Create()); list.AddRange(RebuildFullTextIndex(item.Name)); } }); } return list; } public SQLScriptList RebuildConstraint(Boolean Check) { SQLScriptList list = new SQLScriptList(); if (DefaultConstraint != null) { if ((!Check) || (DefaultConstraint.CanCreate)) list.Add(DefaultConstraint.Create()); list.Add(DefaultConstraint.Drop()); } return list; } public SQLScriptList RebuildSchemaBindingDependencies() { SQLScriptList list = new SQLScriptList(); List items = ((Database)this.Parent.Parent).Dependencies.Find(this.Parent.Id, this.Id, 0); items.ForEach(item => { if ((item.ObjectType == ObjectType.Function) || (item.ObjectType == ObjectType.View)) { if (item.Status != ObjectStatus.Create) list.Add(item.Drop()); if (item.Status != ObjectStatus.Drop) list.Add(item.Create()); } }); return list; } public SQLScriptList Alter(ScriptAction typeStatus) { SQLScriptList list = new SQLScriptList(); string sql = "ALTER TABLE " + Parent.FullName + " ALTER COLUMN " + this.ToSql(false) + "\r\nGO\r\n"; list.Add(sql, 0, typeStatus); return list; } /// /// Compara solo las propiedades de dos campos relacionadas con los Identity. Si existen /// diferencias, devuelve falso, caso contrario, true. /// public static Boolean CompareIdentity(Column origin, Column destination) { if (destination == null) throw new ArgumentNullException("destination"); if (origin == null) throw new ArgumentNullException("origin"); if (origin.IsIdentity != destination.IsIdentity) return false; if (origin.IsIdentityForReplication != destination.IsIdentityForReplication) return false; if (origin.IdentityIncrement != destination.IdentityIncrement) return false; if (origin.IdentitySeed != destination.IdentitySeed) return false; return true; } public static Boolean CompareRule(Column origin, Column destination) { if (destination == null) throw new ArgumentNullException("destination"); if (origin == null) throw new ArgumentNullException("origin"); if ((origin.Rule.Name != null) && (destination.Rule.Name == null)) return false; if ((origin.Rule.Name == null) && (destination.Rule.Name != null)) return false; if (origin.Rule.Name != null) if (!origin.Rule.Name.Equals(destination.Rule.Name)) return false; return true; } /// /// Compara dos campos y devuelve true si son iguales, caso contrario, devuelve false. /// public static Boolean Compare(Column origin, Column destination) { if (destination == null) throw new ArgumentNullException("destination"); if (origin == null) throw new ArgumentNullException("origin"); if (!origin.ComputedFormula.Equals(destination.ComputedFormula)) return false; if (origin.IsComputed != destination.IsComputed) return false; //if (origin.Position != destination.Position) return false; if (!origin.IsComputed) { if (origin.IsXmlDocument != destination.IsXmlDocument) return false; if ((origin.XmlSchema == null) && (destination.XmlSchema != null)) return false; if (origin.XmlSchema != null) if (!origin.XmlSchema.Equals(destination.XmlSchema)) return false; if (origin.IsNullable != destination.IsNullable) return false; if (origin.IsFileStream != destination.IsFileStream) return false; if (origin.IsSparse != destination.IsSparse) return false; if (!origin.Collation.Equals(destination.Collation)) return false; if (!origin.Type.Equals(destination.Type, StringComparison.CurrentCultureIgnoreCase)) return false; //Si el tipo de campo es custom, no compara size del campo. if (!origin.IsUserDefinedType) { if (origin.Precision != destination.Precision) return false; if (origin.Scale != destination.Scale) return false; //Si el tamaņo de un campo Text cambia, entonces por la opcion TextInRowLimit. if ((origin.Size != destination.Size) && (origin.Type.Equals(destination.Type, StringComparison.CurrentCultureIgnoreCase)) && (!origin.Type.Equals("text", StringComparison.CurrentCultureIgnoreCase))) return false; } } else { if (origin.IsPersisted != destination.IsPersisted) return false; } if (!CompareIdentity(origin, destination)) return false; return CompareRule(origin, destination); } public int CompareTo(Column other) { return this.Id.CompareTo(other.Id); } } }