// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. namespace WixToolset.Data.WindowsInstaller { using System; using System.Collections.Generic; using System.Linq; using System.Xml; /// /// Definition of a table in a database. /// public sealed class TableDefinition : IComparable { /// /// Tracks the maximum number of columns supported in a real table. /// This is a Windows Installer limitation. /// public const int MaxColumnsInRealTable = 32; /// /// Creates a table definition. /// /// Name of table to create. /// Column definitions for the table. /// Flag if table is unreal. public TableDefinition(string name, IEnumerable columns, bool unreal = false) { this.Name = name; this.Unreal = unreal; this.Columns = columns.ToArray(); } /// /// Gets the name of the table. /// /// Name of the table. public string Name { get; private set; } /// /// Gets if the table is unreal. /// /// Flag if table is unreal. public bool Unreal { get; private set; } /// /// Gets the collection of column definitions for this table. /// /// Collection of column definitions for this table. public ColumnDefinition[] Columns { get; private set; } /// /// Gets the column definition in the table by index. /// /// Index of column to locate. /// Column definition in the table by index. public ColumnDefinition this[int columnIndex] => this.Columns[columnIndex]; /// /// Compares this table definition to another table definition. /// /// /// Only Windows Installer traits are compared, allowing for updates to WiX-specific table definitions. /// /// The updated to compare with this target definition. /// 0 if the tables' core properties are the same; otherwise, non-0. public int CompareTo(TableDefinition updated) { // by definition, this object is greater than null if (null == updated) { return 1; } // compare the table names var ret = String.Compare(this.Name, updated.Name, StringComparison.Ordinal); // compare the column count if (0 == ret) { // transforms can only add columns ret = Math.Min(0, updated.Columns.Length - this.Columns.Length); // compare name, type, and length of each column for (var i = 0; 0 == ret && this.Columns.Length > i; i++) { var thisColumnDef = this.Columns[i]; var updatedColumnDef = updated.Columns[i]; // Igmore unreal columns when comparing table definitions. if (thisColumnDef.Unreal || updatedColumnDef.Unreal) { continue; } ret = thisColumnDef.CompareTo(updatedColumnDef); } } return ret; } /// /// Parses table definition from xml reader. /// /// Reader to get data from. /// The TableDefintion represented by the Xml. internal static TableDefinition Read(XmlReader reader) { var empty = reader.IsEmptyElement; string name = null; var unreal = false; var bootstrapperApplicationData = false; while (reader.MoveToNextAttribute()) { switch (reader.LocalName) { case "name": name = reader.Value; break; case "unreal": unreal = reader.Value.Equals("yes"); break; } } if (null == name) { throw new XmlException(); } var columns = new List(); var hasPrimaryKeyColumn = false; // parse the child elements if (!empty) { var done = false; while (!done && reader.Read()) { switch (reader.NodeType) { case XmlNodeType.Element: switch (reader.LocalName) { case "columnDefinition": var columnDefinition = ColumnDefinition.Read(reader); columns.Add(columnDefinition); if (columnDefinition.PrimaryKey) { hasPrimaryKeyColumn = true; } break; default: throw new XmlException(); } break; case XmlNodeType.EndElement: done = true; break; } } if (!unreal && !bootstrapperApplicationData && !hasPrimaryKeyColumn) { throw new WixException(ErrorMessages.RealTableMissingPrimaryKeyColumn(SourceLineNumber.CreateFromUri(reader.BaseURI), name)); } if (!done) { throw new XmlException(); } } return new TableDefinition(name, columns.ToArray(), unreal); } /// /// Persists an output in an XML format. /// /// XmlWriter where the Output should persist itself as XML. internal void Write(XmlWriter writer) { writer.WriteStartElement("tableDefinition", TableDefinitionCollection.XmlNamespaceUri); writer.WriteAttributeString("name", this.Name); if (this.Unreal) { writer.WriteAttributeString("unreal", "yes"); } foreach (var columnDefinition in this.Columns) { columnDefinition.Write(writer); } writer.WriteEndElement(); } } }