// --------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // --------------------------------------------------------------------------- // --------------------------------------------------------------------- // // // --------------------------------------------------------------------- namespace Microsoft.Database.Isam { using System; using System.Text; using Microsoft.Isam.Esent.Interop; using Microsoft.Isam.Esent.Interop.Server2003; /// /// A Column Definition contains the schema for a single column. It can be /// used to explore the schema for an existing column and to create the /// definition for a new column. /// public class ColumnDefinition { /// /// The columnid /// private Columnid columnid; /// /// The name /// private string name = null; /// /// The type /// private Type type = null; /// /// The flags /// private ColumnFlags flags = ColumnFlags.None; /// /// The maximum length /// private int maxLength = 0; /// /// The default value /// private object defaultValue = null; /// /// The read only /// private bool readOnly = false; /// /// Initializes a new instance of the class. /// For use when defining a new column. /// /// /// the name of the column to be defined /// /// /// the type of the column to be defined /// /// /// the flags for the column to be defined /// public ColumnDefinition(string name, Type type, ColumnFlags flags) { this.name = name; this.type = type; this.flags = flags; } /// /// Initializes a new instance of the class. /// For use when defining a new column. /// /// /// the name of the column to be defined /// public ColumnDefinition(string name) { this.name = name; } /// /// Initializes a new instance of the class. /// internal ColumnDefinition() { } /// /// Initializes a new instance of the class. /// /// The columnid. internal ColumnDefinition(Columnid columnid) { this.columnid = columnid; this.name = columnid.Name; this.type = columnid.Type; } /// /// Gets a value indicating whether this is a text column, whether it stores Ascii data. If false, text columns store Unicode data. /// public bool IsAscii { get; private set; } /// /// Gets the column ID of the column /// /// /// The column ID is undefined if this column definition will be used /// to define a new column /// public Columnid Columnid { get { return this.columnid; } } /// /// Gets the name of the column. /// public string Name { get { return this.name; } } /// /// Gets or sets the type of the column. /// public Type Type { get { return this.type; } set { this.CheckReadOnly(); this.type = value; } } /// /// Gets or sets the column's flags. /// public ColumnFlags Flags { get { return this.flags; } set { this.CheckReadOnly(); this.flags = value; } } /// /// Gets or sets the max length of the column in bytes. /// /// /// The max length of a fixed column need not be specified when /// defining a new column. /// /// A max length of zero for a variable length column is the same as /// giving that column the largest possible max length. /// /// public int MaxLength { get { return this.maxLength; } set { this.CheckReadOnly(); this.maxLength = value; } } /// /// Gets or sets the default value of the column. /// /// /// If the field corresponding to this column in a given record is /// never set then the value of that field will be the default value of /// the column. /// /// The size of the default value is currently limited to 255 bytes /// by the ISAM. It is also not possible to specify a zero length /// default value. /// /// public object DefaultValue { get { return this.defaultValue; } set { this.CheckReadOnly(); this.defaultValue = value; } } /// /// Gets a value indicating whether this column definition cannot be changed. /// public bool IsReadOnly { get { return this.readOnly; } } /// /// Sets a value indicating whether [read only]. /// /// /// true if [read only]; otherwise, false. /// internal bool ReadOnly { set { this.readOnly = value; } } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { return string.Format("ColumnDefinition({0}, {1})", this.type.Name, this.Name); } /// /// Creates a object representing the column passed in by . /// /// The database. /// Name of the table. /// The object that represents the row in /// the temptable for this particular column. /// /// A object based on the current row in the temptable /// represented by . /// internal static ColumnDefinition Load(IsamDatabase database, string tableName, JET_COLUMNLIST columnList) { lock (database.IsamSession) { using (IsamTransaction trx = new IsamTransaction(database.IsamSession)) { // load info for the column ColumnDefinition columnDefinition = new ColumnDefinition(); JET_SESID sesid = database.IsamSession.Sesid; // As of Sep 2015, JetGetColumnInfoW is only called for Win8+. Even though Unicode should have // worked in Win7, it wasn't reliable until Win8. Encoding encodingOfTextColumns = EsentVersion.SupportsWindows8Features ? Encoding.Unicode : LibraryHelpers.EncodingASCII; string columnName = Api.RetrieveColumnAsString( database.IsamSession.Sesid, columnList.tableid, columnList.columnidcolumnname, encodingOfTextColumns); JET_COLUMNBASE columnbase; Api.JetGetColumnInfo(database.IsamSession.Sesid, database.Dbid, tableName, columnName, out columnbase); columnDefinition.columnid = new Columnid(columnbase); columnDefinition.name = columnDefinition.columnid.Name; columnDefinition.type = columnDefinition.columnid.Type; ColumndefGrbit grbitColumn = (ColumndefGrbit)Api.RetrieveColumnAsUInt32(sesid, columnList.tableid, columnList.columnidgrbit).GetValueOrDefault(); columnDefinition.flags = ColumnFlagsFromGrbits(grbitColumn); columnDefinition.maxLength = Api.RetrieveColumnAsInt32(sesid, columnList.tableid, columnList.columnidcbMax).GetValueOrDefault(); columnDefinition.IsAscii = columnbase.cp == JET_CP.ASCII; byte[] defaultValueBytes = Api.RetrieveColumn(sesid, columnList.tableid, columnList.columnidDefault); Columnid isamColumnid = columnDefinition.columnid; columnDefinition.defaultValue = Converter.ObjectFromBytes( isamColumnid.Coltyp, columnDefinition.IsAscii, defaultValueBytes); columnDefinition.ReadOnly = true; return columnDefinition; } } } /// /// Creates a object representing the column passed in by . /// /// The database. /// Name of the table. /// The object that represents this particular column. /// /// A object based on the object. /// internal static ColumnDefinition Load(IsamDatabase database, string tableName, JET_COLUMNBASE columnBase) { lock (database.IsamSession) { using (IsamTransaction trx = new IsamTransaction(database.IsamSession)) { JET_SESID sesid = database.IsamSession.Sesid; // load info for the column ColumnDefinition columnDefinition = new ColumnDefinition(); columnDefinition.columnid = new Columnid(columnBase); columnDefinition.name = columnDefinition.columnid.Name; columnDefinition.type = columnDefinition.columnid.Type; columnDefinition.flags = ColumnFlagsFromGrbits(columnBase.grbit); columnDefinition.maxLength = (int)columnBase.cbMax; columnDefinition.IsAscii = columnBase.cp == JET_CP.ASCII; // there is currently no efficient means to retrieve the // default value of a specific column from JET. so, we are // going to reach into the catalog and fetch it directly JET_TABLEID tableidCatalog; Api.JetOpenTable( sesid, database.Dbid, "MSysObjects", null, 0, OpenTableGrbit.ReadOnly, out tableidCatalog); Api.JetSetCurrentIndex(sesid, tableidCatalog, "RootObjects"); Api.MakeKey(sesid, tableidCatalog, true, MakeKeyGrbit.NewKey); Api.MakeKey( sesid, tableidCatalog, Converter.BytesFromObject(JET_coltyp.Text, true, columnBase.szBaseTableName), MakeKeyGrbit.None); Api.JetSeek(sesid, tableidCatalog, SeekGrbit.SeekEQ); JET_COLUMNBASE columnbaseCatalog; Api.JetGetTableColumnInfo(sesid, tableidCatalog, "ObjidTable", out columnbaseCatalog); uint objidTable = Api.RetrieveColumnAsUInt32(sesid, tableidCatalog, columnbaseCatalog.columnid).GetValueOrDefault(); Api.JetSetCurrentIndex(sesid, tableidCatalog, "Name"); Api.MakeKey(sesid, tableidCatalog, objidTable, MakeKeyGrbit.NewKey); Api.MakeKey(sesid, tableidCatalog, (short)2, MakeKeyGrbit.None); Api.MakeKey(sesid, tableidCatalog, columnBase.szBaseColumnName, Encoding.ASCII, MakeKeyGrbit.None); Api.JetSeek(sesid, tableidCatalog, SeekGrbit.SeekEQ); Api.JetGetTableColumnInfo(sesid, tableidCatalog, "DefaultValue", out columnbaseCatalog); byte[] defaultValueBytes = Api.RetrieveColumn(sesid, tableidCatalog, columnbaseCatalog.columnid); Columnid isamColumnid = columnDefinition.columnid; columnDefinition.defaultValue = Converter.ObjectFromBytes( isamColumnid.Coltyp, columnDefinition.IsAscii, defaultValueBytes); columnDefinition.ReadOnly = true; return columnDefinition; } } } /// /// Converts to . /// /// The grbit to convert. /// A value equivalent to . private static ColumnFlags ColumnFlagsFromGrbits(ColumndefGrbit grbitColumn) { ColumnFlags flags = ColumnFlags.None; if ((grbitColumn & ColumndefGrbit.ColumnFixed) != 0) { flags = flags | ColumnFlags.Fixed; } if ((grbitColumn & (ColumndefGrbit.ColumnFixed | ColumndefGrbit.ColumnTagged)) == 0) { flags = flags | ColumnFlags.Variable; } if ((grbitColumn & ColumndefGrbit.ColumnTagged) != 0) { flags = flags | ColumnFlags.Sparse; } if ((grbitColumn & ColumndefGrbit.ColumnNotNULL) != 0) { flags = flags | ColumnFlags.NonNull; } if ((grbitColumn & ColumndefGrbit.ColumnVersion) != 0) { flags = flags | ColumnFlags.Version; } if ((grbitColumn & ColumndefGrbit.ColumnAutoincrement) != 0) { flags = flags | ColumnFlags.AutoIncrement; } if ((grbitColumn & ColumndefGrbit.ColumnUpdatable) != 0) { flags = flags | ColumnFlags.Updatable; } if ((grbitColumn & ColumndefGrbit.ColumnMultiValued) != 0) { flags = flags | ColumnFlags.MultiValued; } if ((grbitColumn & ColumndefGrbit.ColumnEscrowUpdate) != 0) { flags = flags | ColumnFlags.EscrowUpdate; } if ((grbitColumn & ColumndefGrbit.ColumnFinalize) != 0) { flags = flags | ColumnFlags.Finalize; } if ((grbitColumn & ColumndefGrbit.ColumnUserDefinedDefault) != 0) { flags = flags | ColumnFlags.UserDefinedDefault; } if ((grbitColumn & Server2003Grbits.ColumnDeleteOnZero) != 0) { flags = flags | ColumnFlags.DeleteOnZero; } return flags; } /// /// Checks the read only status. /// /// this column definition cannot be changed private void CheckReadOnly() { if (this.readOnly) { throw new NotSupportedException("this column definition cannot be changed"); } } } }