// --------------------------------------------------------------------------- // // 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.Vista; /// /// A Database is a file used by the ISAM to store data. It is organized /// into tables which are in turn comprised of columns and indices and /// contain data in the form of records. The database's schema can be /// enumerated and manipulated by this object. Also, the database's /// tables can be opened for access by this object. /// /// DatabaseCommon is the common root class for all types of databases. /// Currently, there are two types: Database which provides access to /// ordinary databases and TemporaryDatabase which provides access to /// temporary databases. /// /// public abstract class DatabaseCommon : IDisposable { /// /// The schema update identifier /// private static long schemaUpdateID = 0; /// /// The session /// private readonly IsamSession isamSession; /// /// The cleanup /// private bool cleanup = false; /// /// The disposed /// private bool disposed = false; /// /// Initializes a new instance of the class. /// /// The session. internal DatabaseCommon(IsamSession isamSession) { lock (isamSession) { this.isamSession = isamSession; this.cleanup = true; } } /// /// Finalizes an instance of the DatabaseCommon class /// ~DatabaseCommon() { this.Dispose(false); } /// /// Gets the session that created this database /// public IsamSession IsamSession { get { return this.isamSession; } } /// /// Gets a collection of tables in the database. /// /// a collection of tables in the database public abstract TableCollection Tables { get; } /// /// Gets or sets the schema update identifier. /// /// /// The schema update identifier. /// internal static long SchemaUpdateID { get { return schemaUpdateID; } set { schemaUpdateID = value; } } /// /// Gets or sets a value indicating whether [disposed]. /// /// /// true if [disposed]; otherwise, false. /// internal virtual bool Disposed { get { return this.disposed || this.isamSession.Disposed; } set { this.disposed = value; } } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { lock (this) { this.Dispose(true); } GC.SuppressFinalize(this); } /// /// Creates a single table with the specified definition in the database /// /// The table definition. public abstract void CreateTable(TableDefinition tableDefinition); /// /// Deletes a single table in the database /// /// Name of the table. /// /// It is currently not possible to delete a table that is being used /// by a Cursor. All such Cursors must be disposed before the /// table can be successfully deleted. /// public abstract void DropTable(string tableName); /// /// Determines if a given table exists in the database /// /// Name of the table. /// /// true if the table was found, false otherwise /// public abstract bool Exists(string tableName); /// /// Opens a cursor over the specified table. /// /// the name of the table to be opened /// a cursor over the specified table in this database public abstract Cursor OpenCursor(string tableName); /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// void IDisposable.Dispose() { this.Dispose(); } /// /// Converts to a . /// /// The column definition. /// The equivalent of . /// Cannot map this type to a native column type internal static JET_coltyp ColtypFromColumnDefinition(ColumnDefinition columnDefinition) { if (columnDefinition.Type == typeof(bool)) { return JET_coltyp.Bit; } else if (columnDefinition.Type == typeof(byte)) { return JET_coltyp.UnsignedByte; } else if (columnDefinition.Type == typeof(char)) { return VistaColtyp.UnsignedShort; } else if (columnDefinition.Type == typeof(System.DateTime)) { return JET_coltyp.DateTime; } else if (columnDefinition.Type == typeof(double)) { return JET_coltyp.IEEEDouble; } else if (columnDefinition.Type == typeof(short)) { return JET_coltyp.Short; } else if (columnDefinition.Type == typeof(int)) { return JET_coltyp.Long; } else if (columnDefinition.Type == typeof(long)) { return VistaColtyp.LongLong; } else if (columnDefinition.Type == typeof(float)) { return JET_coltyp.IEEESingle; } else if (columnDefinition.Type == typeof(string)) { if (columnDefinition.MaxLength > 0 && columnDefinition.MaxLength <= 255) { return JET_coltyp.Text; } else { return JET_coltyp.LongText; } } else if (columnDefinition.Type == typeof(ushort)) { return VistaColtyp.UnsignedShort; } else if (columnDefinition.Type == typeof(uint)) { return VistaColtyp.UnsignedLong; } else if (columnDefinition.Type == typeof(byte[])) { if (columnDefinition.MaxLength > 0 && columnDefinition.MaxLength <= 255) { return JET_coltyp.Binary; } else { return JET_coltyp.LongBinary; } } else if (columnDefinition.Type == typeof(System.Guid)) { return VistaColtyp.GUID; } else { throw new ArgumentException("Cannot map this type to a native column type"); } } /// /// Converts a to a double-null-terminated string usable by the /// C API to create an index. /// /// The index definition. /// A double-null-terminated string usable by the C API. internal static string IndexKeyFromIndexDefinition(IndexDefinition indexDefinition) { StringBuilder sb = new StringBuilder(); foreach (KeyColumn keyColumn in indexDefinition.KeyColumns) { sb.AppendFormat("{0}{1}\0", keyColumn.IsAscending ? "+" : "-", keyColumn.Name); } // Index keys need to be double-null terminated. The following allows us to use 'string.Length' // directly instead of having to add 1/2 for the terminating NULLs. sb.Append('\0'); return sb.ToString(); } /// /// Converts a to an array of objects. /// /// The index definition. /// An array of objects. internal static JET_CONDITIONALCOLUMN[] ConditionalColumnsFromIndexDefinition(IndexDefinition indexDefinition) { JET_CONDITIONALCOLUMN[] conditionalColumns = new JET_CONDITIONALCOLUMN[indexDefinition.ConditionalColumns.Count]; for (int col = 0; col < conditionalColumns.Length; ++col) { conditionalColumns[col] = new JET_CONDITIONALCOLUMN(); } int i = 0; foreach (ConditionalColumn conditionalColumn in indexDefinition.ConditionalColumns) { conditionalColumns[i].szColumnName = conditionalColumn.Name; conditionalColumns[i].grbit = conditionalColumn.MustBeNull ? ConditionalColumnGrbit.ColumnMustBeNull : ConditionalColumnGrbit.ColumnMustBeNonNull; i++; } return conditionalColumns; } /// /// Retrieves the options from an object. /// /// The index definition. /// The options. internal static CreateIndexGrbit GrbitFromIndexDefinition(IndexDefinition indexDefinition) { CreateIndexGrbit grbit = CreateIndexGrbit.None; // We always provide unicode normalization configuration, // but ManagedEsent will set the grbit automatically. // grbit = grbit | VistaGrbits.IndexUnicode; // We always provide a max key length, but ManagedEsent will // set the grbit automatically. // grbit = grbit | VistaGrbits.IndexKeyMost; // we always do cross product indexing of multi-values grbit = grbit | VistaGrbits.IndexCrossProduct; if ((indexDefinition.Flags & IndexFlags.Unique) != 0) { grbit = grbit | CreateIndexGrbit.IndexUnique; } if ((indexDefinition.Flags & IndexFlags.Primary) != 0) { grbit = grbit | CreateIndexGrbit.IndexPrimary; } if ((indexDefinition.Flags & IndexFlags.DisallowNull) != 0) { grbit = grbit | CreateIndexGrbit.IndexDisallowNull; } if ((indexDefinition.Flags & IndexFlags.IgnoreNull) != 0) { grbit = grbit | CreateIndexGrbit.IndexIgnoreNull; } if ((indexDefinition.Flags & IndexFlags.IgnoreAnyNull) != 0) { grbit = grbit | CreateIndexGrbit.IndexIgnoreAnyNull; } if ((indexDefinition.Flags & IndexFlags.SortNullsHigh) != 0) { grbit = grbit | CreateIndexGrbit.IndexSortNullsHigh; } if ((indexDefinition.Flags & IndexFlags.DisallowTruncation) != 0) { grbit = grbit | VistaGrbits.IndexDisallowTruncation; } // AllowTruncation is an Isam-only flag, and we require it. if ((indexDefinition.Flags & IndexFlags.AllowTruncation) == 0) { grbit = grbit | VistaGrbits.IndexDisallowTruncation; } return grbit; } /// /// Checks the name. /// /// The name. /// The e. internal static void CheckName(string name, System.Exception e) { // we accept null names as valid at this level if (name == null) { return; } // valid names must be between 1 and 64 chars in length if (name.Length < 1 || name.Length > 64) { throw e; } // valid names must be in the ASCII range and cannot contain any // of the following characters: // - control chars (< 0x20) // - ! // - . // - [ // - ] for (int i = 0; i < name.Length - 1; i++) { if (name[i] < 0x20 || name[i] == '!' || name[i] == '.' || name[i] == '[' || name[i] == ']' || name[i] > 0xFF) { throw e; } } // valid names must not begin with a space if (name[0] == ' ') { throw e; } } /// /// Converts a major/minor/build number of ESENT to a single number for comparison. /// /// The major. /// The minor. /// The build. /// The update. /// An integer value equivalent of the version specified. /// Returns a different value from /// internal static long ESENTVersion(int major, int minor, int build, int update) { long version = ((long)major << 28) + (((long)minor << 24) & 0x0F) + ((long)build << 8) + ((long)update); return version; } /// /// Converts a major/minor/build number of ESE to a single number for comparison. /// /// The major. /// The minor. /// The build. /// The update. /// An integer value equivalent of the version specified. /// Returns a different value from /// internal static long ESEVersion(int major, int minor, int build, int update) { long version = (((long)major & 0x1F) << 27) + (((long)minor & 0x1F) << 22) + (((long)build & 0x3FFF) << 8) + ((long)update & 0xFF); return version; } /// /// Checks the engine version. /// /// The session. /// The minimum ESENT version required. /// The minimum ESE version required. /// Whether the current engine is greater than or equal to the required version. internal static bool CheckEngineVersion( IsamSession isamSession, long versionESENT, long versionESE) { #if (ESENT) long version = versionESENT; #else long version = versionESE; #endif uint engineVersion; Api.JetGetVersion(isamSession.Sesid, out engineVersion); return engineVersion >= version; } /// /// Checks the engine version. /// /// The session. /// The version esent. /// The version ese. /// The e. internal static void CheckEngineVersion( IsamSession isamSession, long versionESENT, long versionESE, System.Exception e) { if (!CheckEngineVersion(isamSession, versionESENT, versionESE)) { throw e; } } /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { lock (this.isamSession) { if (!this.Disposed) { if (this.cleanup) { this.cleanup = false; } this.Disposed = true; } } } /// /// Checks the disposed. /// /// /// Thrown when the object is already disposed. /// private void CheckDisposed() { lock (this.isamSession) { if (this.Disposed) { throw new ObjectDisposedException(this.GetType().Name); } } } } }