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