// ---------------------------------------------------------------------------
//
// 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);
}
}
}
}
}