/// <summary> /// /// </summary> /// <param name="dbPath">dbFilePath must point to the DIT file on the local computer.</param> /// <param name="logPath">The path should point to a writeable folder on the local computer, where ESE log files will be created. If not specified, then temp folder will be used.</param> public DirectoryContext(string dbFilePath, bool readOnly, string logDirectoryPath = null) { if (!File.Exists(dbFilePath)) { // TODO: Extract as resource throw new FileNotFoundException("The specified database file does not exist.", dbFilePath); } int pageSize = DirectoryContext.GetDBPageSize(dbFilePath); string dbDirectoryPath = Path.GetDirectoryName(dbFilePath); string checkpointDirectoryPath = dbDirectoryPath; string tempDirectoryPath = dbDirectoryPath; if (logDirectoryPath != null) { if (!Directory.Exists(logDirectoryPath)) { // TODO: Extract as resource throw new FileNotFoundException("The specified log directory does not exist.", logDirectoryPath); } } else { logDirectoryPath = dbDirectoryPath; } // TODO: Exception handling? // HACK: IsamInstance constructor throws AccessDenied Exception when the path does not end with a backslash. // TODO: Const isam instance name string instanceName = "DSInternals"; this.instance = new IsamInstance(AddPathSeparator(checkpointDirectoryPath), AddPathSeparator(logDirectoryPath), AddPathSeparator(tempDirectoryPath), ADConstants.EseBaseName, instanceName, readOnly, pageSize); try { var isamParameters = this.instance.IsamSystemParameters; // TODO: Add param explanations isamParameters.LogFileSize = ADConstants.EseLogFileSize; isamParameters.DeleteOutOfRangeLogs = true; isamParameters.EnableIndexChecking = 1; isamParameters.EnableIndexCleanup = true; isamParameters.CircularLog = true; // TODO: Configure additional ISAM parameters // this.instance.IsamSystemParameters.EnableOnlineDefrag = false; // JET_paramDeleteOldLogs = 1 this.session = this.instance.CreateSession(); this.session.AttachDatabase(dbFilePath); this.attachedDatabasePath = dbFilePath; this.database = this.session.OpenDatabase(dbFilePath); this.Schema = new DirectorySchema(this.database); this.SecurityDescriptorRersolver = new SecurityDescriptorRersolver(this.database); this.DistinguishedNameResolver = new DistinguishedNameResolver(this.database, this.Schema); this.LinkResolver = new LinkResolver(this.database, this.Schema); this.DomainController = new DomainController(this); } catch { // Free resources if anything failed this.Dispose(); throw; } }
// TODO: Internal? // TODO: ISchema public DirectorySchema(IsamDatabase database) { TableDefinition dataTable = database.Tables[ADConstants.DataTableName]; this.LoadColumnList(dataTable.Columns); this.LoadAttributeIndices(dataTable.Indices); using (var cursor = database.OpenCursor(ADConstants.DataTableName)) { this.LoadClassList(cursor); this.LoadAttributeProperties(cursor); this.LoadPrefixMap(cursor); } // TODO: Load Ext-Int Map from hiddentable }
public LinkResolver(IsamDatabase database, DirectorySchema schema) { this.schema = schema; this.cursor = database.OpenCursor(ADConstants.LinkTableName); if (this.cursor.TableDefinition.Indices.Contains(linkIndex2008)) { this.cursor.SetCurrentIndex(linkIndex2008); } else { // Fallback to the old index if the newer one does not exist cursor.SetCurrentIndex(linkIndex2003); } // TODO: Load column ids instead of names }
/// <summary> /// Creates a <see cref="ColumnDefinition"/> object representing the column passed in by <paramref name="columnList"/>. /// </summary> /// <param name="database">The database.</param> /// <param name="tableName">Name of the table.</param> /// <param name="columnList">The <see cref="JET_COLUMNLIST"/> object that represents the row in /// the temptable for this particular column.</param> /// <returns> /// A <see cref="ColumnDefinition"/> object based on the current row in the temptable /// represented by <paramref name="columnList"/>. /// </returns> 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); } } }
/// <summary> /// Loads the specified table specified in the <see cref="JET_OBJECTINFO"/> object. /// </summary> /// <param name="database">The database.</param> /// <param name="tableName">Name of the table.</param> /// <param name="objectInfo">The object information.</param> /// <returns>A <see cref="TableDefinition"/> corresponding to the table specified in <paramref name="objectInfo"/>.</returns> internal static TableDefinition Load(IsamDatabase database, string tableName, JET_OBJECTINFO objectInfo) { lock (database.IsamSession) { // save the database TableDefinition tableDefinition = new TableDefinition(); tableDefinition.database = database; // load info for the table tableDefinition.name = tableName; // create our column and index collections tableDefinition.columnCollection = new ColumnCollection(database, tableDefinition.name); tableDefinition.indexCollection = new IndexCollection(database, tableDefinition.name); return(tableDefinition); } }
/// <summary> /// Loads the specified table specified in the <see cref="JET_OBJECTLIST"/> object. /// </summary> /// <param name="database">The database.</param> /// <param name="objectList">The object list.</param> /// <returns>A <see cref="TableDefinition"/> corresponding to the table specified in <paramref name="objectList"/>.</returns> internal static TableDefinition Load(IsamDatabase database, JET_OBJECTLIST objectList) { lock (database.IsamSession) { // save the database TableDefinition tableDefinition = new TableDefinition(); tableDefinition.database = database; // load info for the table tableDefinition.name = Api.RetrieveColumnAsString( database.IsamSession.Sesid, objectList.tableid, objectList.columnidobjectname); // create our column and index collections tableDefinition.columnCollection = new ColumnCollection(database, tableDefinition.name); tableDefinition.indexCollection = new IndexCollection(database, tableDefinition.name); return(tableDefinition); } }
/// <summary> /// Initializes a new instance of the <see cref="TableEnumerator"/> class. /// </summary> /// <param name="database">The database.</param> internal TableEnumerator(IsamDatabase database) { this.database = database; this.enumerator = null; }
/// <summary> /// Initializes a new instance of the <see cref="ColumnEnumerator"/> class. /// </summary> /// <param name="database">The database.</param> /// <param name="tableName">Name of the table.</param> internal ColumnEnumerator(IsamDatabase database, string tableName) { this.database = database; this.tableName = tableName; this.enumerator = null; }
/// <summary> /// Initializes a new instance of the <see cref="TableCollection"/> class. /// </summary> public TableCollection() { this.database = null; }
/// <summary> /// Initializes a new instance of the <see cref="ColumnEnumerator"/> class. /// </summary> /// <param name="enumerator">The enumerator.</param> internal ColumnEnumerator(IDictionaryEnumerator enumerator) { this.database = null; this.enumerator = enumerator; }
/// <summary> /// Initializes a new instance of the <see cref="ColumnCollection"/> class. /// </summary> /// <param name="database">The database.</param> /// <param name="tableName">Name of the table.</param> internal ColumnCollection(IsamDatabase database, string tableName) { this.database = database; this.tableName = tableName; this.readOnly = true; }
public SecurityDescriptorRersolver(IsamDatabase database) { this.cursor = database.OpenCursor(ADConstants.SecurityDescriptorTableName); this.cursor.SetCurrentIndex(SdIndex); }
/// <summary> /// Initializes a new instance of the <see cref="IndexCollection"/> class. /// </summary> /// <param name="database">The database.</param> /// <param name="tableName">Name of the table.</param> internal IndexCollection(IsamDatabase database, string tableName) { this.database = database; this.tableName = tableName; this.readOnly = true; }
/// <summary> /// Initializes a new instance of the <see cref="Cursor"/> class. /// </summary> /// <param name="isamSession">The session.</param> /// <param name="database">The database.</param> /// <param name="tableName">Name of the table.</param> /// <param name="grbit">The grbit.</param> internal Cursor(IsamSession isamSession, IsamDatabase database, string tableName, OpenTableGrbit grbit) { lock (isamSession) { this.isamSession = isamSession; this.database = database; this.tableName = tableName; Api.JetOpenTable(isamSession.Sesid, database.Dbid, tableName, null, 0, grbit, out this.tableid); this.cleanup = true; this.record = new ColumnAccessor(this, isamSession, this.tableid, RetrieveColumnGrbit.None); this.editRecord = new ColumnAccessor(this, isamSession, this.tableid, RetrieveColumnGrbit.RetrieveCopy); this.indexRecord = new ColumnAccessor(this, isamSession, this.tableid, RetrieveColumnGrbit.RetrieveFromIndex); this.MoveBeforeFirst(); } }
/// <summary> /// Initializes a new instance of the <see cref="ColumnCollection"/> class. /// </summary> public ColumnCollection() { this.database = null; }
/// <summary> /// Creates a <see cref="ColumnDefinition"/> object representing the column passed in by <paramref name="columnBase"/>. /// </summary> /// <param name="database">The database.</param> /// <param name="tableName">Name of the table.</param> /// <param name="columnBase">The <see cref="JET_COLUMNBASE"/> object that represents this particular column.</param> /// <returns> /// A <see cref="ColumnDefinition"/> object based on the <paramref name="columnBase"/> object. /// </returns> 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); } } }
/// <summary> /// Initializes a new instance of the <see cref="IndexCollection"/> class. /// </summary> public IndexCollection() { this.database = null; }
/// <summary> /// Initializes a new instance of the <see cref="TableCollection"/> class. /// </summary> /// <param name="database">The database.</param> internal TableCollection(IsamDatabase database) { this.database = database; this.readOnly = true; }
/// <summary> /// Creates an <see cref="IndexDefinition"/> object from the specified <paramref name="indexList"/>. /// </summary> /// <param name="database">The database.</param> /// <param name="tableName">Name of the table.</param> /// <param name="indexList">The index list.</param> /// <returns>An <see cref="IndexDefinition"/> object represting the specified index.</returns> internal static IndexDefinition Load(IsamDatabase database, string tableName, JET_INDEXLIST indexList) { lock (database.IsamSession) { JET_SESID sesid = database.IsamSession.Sesid; using (IsamTransaction trx = new IsamTransaction(database.IsamSession)) { // load info for the index IndexDefinition indexDefinition = new IndexDefinition(); indexDefinition.name = Api.RetrieveColumnAsString( sesid, indexList.tableid, indexList.columnidindexname); CreateIndexGrbit grbitIndex = (CreateIndexGrbit)Api.RetrieveColumnAsUInt32(sesid, indexList.tableid, indexList.columnidgrbitIndex); indexDefinition.flags = IndexFlagsFromGrbits(grbitIndex); Api.JetGetIndexInfo( sesid, database.Dbid, tableName, indexDefinition.name, out indexDefinition.density, JET_IdxInfo.SpaceAlloc); int lcid; Api.JetGetIndexInfo( database.IsamSession.Sesid, database.Dbid, tableName, indexDefinition.name, out lcid, JET_IdxInfo.LCID); indexDefinition.cultureInfo = new CultureInfo(lcid); indexDefinition.compareOptions = Conversions.CompareOptionsFromLCMapFlags( Api.RetrieveColumnAsUInt32( database.IsamSession.Sesid, indexList.tableid, indexList.columnidLCMapFlags).GetValueOrDefault()); // CONSIDER: move this workaround into Isam.Interop try { ushort maxKeyLength; Api.JetGetIndexInfo( database.IsamSession.Sesid, database.Dbid, tableName, indexDefinition.name, out maxKeyLength, JET_IdxInfo.KeyMost); indexDefinition.maxKeyLength = maxKeyLength; } catch (EsentInvalidParameterException) { indexDefinition.maxKeyLength = 255; } catch (EsentColumnNotFoundException) { indexDefinition.maxKeyLength = 255; } // load info for each key column in the index int currentColumn = 0; int totalNumberColumns = Api.RetrieveColumnAsInt32(sesid, indexList.tableid, indexList.columnidcColumn).GetValueOrDefault(); indexDefinition.keyColumnCollection = new KeyColumnCollection(); do { // load info for this key column IndexKeyGrbit grbitColumn = (IndexKeyGrbit)Api.RetrieveColumnAsUInt32(sesid, indexList.tableid, indexList.columnidgrbitColumn); bool isAscending = (grbitColumn & IndexKeyGrbit.Descending) == 0; string columnName = Api.RetrieveColumnAsString( sesid, indexList.tableid, indexList.columnidcolumnname); JET_COLUMNBASE columnbase; Api.JetGetColumnInfo(sesid, database.Dbid, tableName, columnName, out columnbase); indexDefinition.keyColumnCollection.Add(new KeyColumn(new Columnid(columnbase), isAscending)); // move onto the next key column definition, unless it is // the last key column if (currentColumn != totalNumberColumns - 1) { Api.TryMoveNext(sesid, indexList.tableid); } }while (++currentColumn < totalNumberColumns); indexDefinition.keyColumnCollection.ReadOnly = true; // Vista: There is currently no efficient means to retrieve the // conditional columns for an index from JET. so, we are // going to reach into the catalog and fetch them directly. // // FUTURE: Windows 7 introduced Windows7IdxInfo.CreateIndex and Windows7IdxInfo.CreateIndex2 (and // Win8 has Windows8IdxInfo.CreateIndex3). Consider retrieving the conditional columns with that // API and converting the results. But that does not solve the problem for Vista. indexDefinition.conditionalColumnCollection = new ConditionalColumnCollection(); JET_TABLEID tableidCatalog; Api.JetOpenTable( database.IsamSession.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, tableName, Encoding.ASCII, MakeKeyGrbit.None); Api.JetSeek(sesid, tableidCatalog, SeekGrbit.SeekEQ); JET_COLUMNID columnidTemp = Api.GetTableColumnid(sesid, tableidCatalog, "ObjidTable"); int objidTable = Api.RetrieveColumnAsInt32(sesid, tableidCatalog, columnidTemp).GetValueOrDefault(); Api.JetSetCurrentIndex(sesid, tableidCatalog, "Name"); Api.MakeKey(sesid, tableidCatalog, objidTable, MakeKeyGrbit.NewKey); Api.MakeKey(sesid, tableidCatalog, (short)3, MakeKeyGrbit.None); Api.MakeKey(sesid, tableidCatalog, indexDefinition.name, Encoding.ASCII, MakeKeyGrbit.None); Api.JetSeek(sesid, tableidCatalog, SeekGrbit.SeekEQ); columnidTemp = Api.GetTableColumnid(sesid, tableidCatalog, "Flags"); int indexFlagsBytes = Api.RetrieveColumnAsInt32(sesid, tableidCatalog, columnidTemp).GetValueOrDefault(); columnidTemp = Api.GetTableColumnid(sesid, tableidCatalog, "ConditionalColumns"); byte[] conditionalColumnsBytes = Api.RetrieveColumn(sesid, tableidCatalog, columnidTemp); for (int ib = 0; conditionalColumnsBytes != null && ib < conditionalColumnsBytes.Length; ib += 4) { uint colid; bool mustBeNull; JET_COLUMNBASE columnBase; // fIDXExtendedColumns if ((indexFlagsBytes & 0xffff0000) == 0x00010000) { // fIDXSEGTemplateColumn if ((conditionalColumnsBytes[ib + 0] & 0x80) != 0) { // fCOLUMNIDTemplate colid = 0x80000000 | (uint)(conditionalColumnsBytes[ib + 3] << 8) | (uint)conditionalColumnsBytes[ib + 2]; } else { colid = (uint)(conditionalColumnsBytes[ib + 3] << 8) | (uint)conditionalColumnsBytes[ib + 2]; } // fIDXSEGMustBeNull if ((conditionalColumnsBytes[ib + 0] & 0x20) != 0) { mustBeNull = true; } else { mustBeNull = false; } } else { // do not load conditional columns from an unknown format continue; } JET_COLUMNID castedColid = JET_COLUMNID.CreateColumnidFromNativeValue(unchecked ((int)colid)); VistaApi.JetGetColumnInfo( database.IsamSession.Sesid, database.Dbid, tableName, castedColid, out columnBase); indexDefinition.conditionalColumnCollection.Add(new ConditionalColumn(new Columnid(columnBase), mustBeNull)); } indexDefinition.conditionalColumnCollection.ReadOnly = true; indexDefinition.ReadOnly = true; return(indexDefinition); } } }
protected virtual void Dispose(bool disposing) { if (!disposing) { // Do nothing return; } if(this.LinkResolver != null) { this.LinkResolver.Dispose(); this.LinkResolver = null; } if (this.SecurityDescriptorRersolver != null) { this.SecurityDescriptorRersolver.Dispose(); this.SecurityDescriptorRersolver = null; } if (this.DistinguishedNameResolver != null) { this.DistinguishedNameResolver.Dispose(); this.DistinguishedNameResolver = null; } if (this.DomainController != null) { this.DomainController.Dispose(); this.DomainController = null; } if (this.database != null) { this.database.Dispose(); this.database = null; } if (this.session != null) { if (this.attachedDatabasePath != null) { this.session.DetachDatabase(this.attachedDatabasePath); this.attachedDatabasePath = null; } this.session.Dispose(); this.session = null; } if (this.instance != null) { this.instance.Dispose(); this.instance = null; } }
public DistinguishedNameResolver(IsamDatabase database, DirectorySchema schema) { this.dnCache = new Dictionary<int, string>(); this.schema = schema; this.cursor = database.OpenCursor(ADConstants.DataTableName); }
/// <summary> /// Creates a single table with the specified definition in the database /// </summary> /// <param name="tableDefinition">The table definition.</param> public override void CreateTable(TableDefinition tableDefinition) { lock (this.IsamSession) { this.CheckDisposed(); using (IsamTransaction trx = new IsamTransaction(this.IsamSession)) { // FUTURE-2013/11/15-martinc: Consider using JetCreateTableColumnIndex(). It would be // a bit faster because it's only a single managed/native transition. // Hard-code the initial space and density. JET_TABLEID tableid; Api.JetCreateTable(this.IsamSession.Sesid, this.dbid, tableDefinition.Name, 16, 90, out tableid); foreach (ColumnDefinition columnDefinition in tableDefinition.Columns) { JET_COLUMNDEF columndef = new JET_COLUMNDEF(); columndef.coltyp = IsamDatabase.ColtypFromColumnDefinition(columnDefinition); columndef.cp = JET_CP.Unicode; columndef.cbMax = columnDefinition.MaxLength; columndef.grbit = Converter.ColumndefGrbitFromColumnFlags(columnDefinition.Flags); byte[] defaultValueBytes = Converter.BytesFromObject( columndef.coltyp, false /*ASCII */, columnDefinition.DefaultValue); JET_COLUMNID columnid; int defaultValueLength = (defaultValueBytes == null) ? 0 : defaultValueBytes.Length; Api.JetAddColumn( this.IsamSession.Sesid, tableid, columnDefinition.Name, columndef, defaultValueBytes, defaultValueLength, out columnid); } foreach (IndexDefinition indexDefinition in tableDefinition.Indices) { JET_INDEXCREATE[] indexcreates = new JET_INDEXCREATE[1]; indexcreates[0] = new JET_INDEXCREATE(); indexcreates[0].szIndexName = indexDefinition.Name; indexcreates[0].szKey = IsamDatabase.IndexKeyFromIndexDefinition(indexDefinition); indexcreates[0].cbKey = indexcreates[0].szKey.Length; indexcreates[0].grbit = IsamDatabase.GrbitFromIndexDefinition(indexDefinition); indexcreates[0].ulDensity = indexDefinition.Density; indexcreates[0].pidxUnicode = new JET_UNICODEINDEX(); indexcreates[0].pidxUnicode.lcid = indexDefinition.CultureInfo.LCID; indexcreates[0].pidxUnicode.dwMapFlags = (uint)Converter.UnicodeFlagsFromCompareOptions(indexDefinition.CompareOptions); indexcreates[0].rgconditionalcolumn = IsamDatabase.ConditionalColumnsFromIndexDefinition(indexDefinition); indexcreates[0].cConditionalColumn = indexcreates[0].rgconditionalcolumn.Length; indexcreates[0].cbKeyMost = indexDefinition.MaxKeyLength; Api.JetCreateIndex2(this.IsamSession.Sesid, tableid, indexcreates, indexcreates.Length); } // The initially-created tableid is opened exclusively. Api.JetCloseTable(this.IsamSession.Sesid, tableid); trx.Commit(); DatabaseCommon.SchemaUpdateID++; } } }
/// <summary> /// Creates a <see cref="ColumnDefinition"/> object representing the column passed in by <paramref name="columnList"/>. /// </summary> /// <param name="database">The database.</param> /// <param name="tableName">Name of the table.</param> /// <param name="columnList">The <see cref="JET_COLUMNLIST"/> object that represents the row in /// the temptable for this particular column.</param> /// <returns> /// A <see cref="ColumnDefinition"/> object based on the current row in the temptable /// represented by <paramref name="columnList"/>. /// </returns> 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; // If we use the wide API (Vista+), then the temp table will be in UTF-16. // Technically, this should have worked in Vista. But there was a bug, and // it was fixed after Windows 7. Encoding encodingOfTextColumns = EsentVersion.SupportsWindows8Features ? Encoding.Unicode : Encoding.ASCII; 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; } } }
/// <summary> /// Creates a <see cref="ColumnDefinition"/> object representing the column passed in by <paramref name="columnBase"/>. /// </summary> /// <param name="database">The database.</param> /// <param name="tableName">Name of the table.</param> /// <param name="columnBase">The <see cref="JET_COLUMNBASE"/> object that represents this particular column.</param> /// <returns> /// A <see cref="ColumnDefinition"/> object based on the <paramref name="columnBase"/> object. /// </returns> 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; } } }
/// <summary> /// Creates an <see cref="IndexDefinition"/> object from the specified <paramref name="indexList"/>. /// </summary> /// <param name="database">The database.</param> /// <param name="tableName">Name of the table.</param> /// <param name="indexList">The index list.</param> /// <returns>An <see cref="IndexDefinition"/> object represting the specified index.</returns> internal static IndexDefinition Load(IsamDatabase database, string tableName, JET_INDEXLIST indexList) { lock (database.IsamSession) { JET_SESID sesid = database.IsamSession.Sesid; using (IsamTransaction trx = new IsamTransaction(database.IsamSession)) { // load info for the index IndexDefinition indexDefinition = new IndexDefinition(); indexDefinition.name = Api.RetrieveColumnAsString( sesid, indexList.tableid, indexList.columnidindexname); CreateIndexGrbit grbitIndex = (CreateIndexGrbit)Api.RetrieveColumnAsUInt32(sesid, indexList.tableid, indexList.columnidgrbitIndex); indexDefinition.flags = IndexFlagsFromGrbits(grbitIndex); Api.JetGetIndexInfo( sesid, database.Dbid, tableName, indexDefinition.name, out indexDefinition.density, JET_IdxInfo.SpaceAlloc); int lcid; Api.JetGetIndexInfo( database.IsamSession.Sesid, database.Dbid, tableName, indexDefinition.name, out lcid, JET_IdxInfo.LCID); indexDefinition.cultureInfo = new CultureInfo(lcid); indexDefinition.compareOptions = Conversions.CompareOptionsFromLCMapFlags( Api.RetrieveColumnAsUInt32( database.IsamSession.Sesid, indexList.tableid, indexList.columnidLCMapFlags).GetValueOrDefault()); // CONSIDER: move this workaround into Isam.Interop try { ushort maxKeyLength; Api.JetGetIndexInfo( database.IsamSession.Sesid, database.Dbid, tableName, indexDefinition.name, out maxKeyLength, JET_IdxInfo.KeyMost); indexDefinition.maxKeyLength = maxKeyLength; } catch (EsentInvalidParameterException) { indexDefinition.maxKeyLength = 255; } catch (EsentColumnNotFoundException) { indexDefinition.maxKeyLength = 255; } // load info for each key column in the index int currentColumn = 0; int totalNumberColumns = Api.RetrieveColumnAsInt32(sesid, indexList.tableid, indexList.columnidcColumn).GetValueOrDefault(); indexDefinition.keyColumnCollection = new KeyColumnCollection(); do { // load info for this key column IndexKeyGrbit grbitColumn = (IndexKeyGrbit)Api.RetrieveColumnAsUInt32(sesid, indexList.tableid, indexList.columnidgrbitColumn); bool isAscending = (grbitColumn & IndexKeyGrbit.Descending) == 0; string columnName = Api.RetrieveColumnAsString( sesid, indexList.tableid, indexList.columnidcolumnname); JET_COLUMNBASE columnbase; Api.JetGetColumnInfo(sesid, database.Dbid, tableName, columnName, out columnbase); indexDefinition.keyColumnCollection.Add(new KeyColumn(new Columnid(columnbase), isAscending)); // move onto the next key column definition, unless it is // the last key column if (currentColumn != totalNumberColumns - 1) { Api.TryMoveNext(sesid, indexList.tableid); } } while (++currentColumn < totalNumberColumns); indexDefinition.keyColumnCollection.ReadOnly = true; // Vista: There is currently no efficient means to retrieve the // conditional columns for an index from JET. so, we are // going to reach into the catalog and fetch them directly. // // FUTURE: Windows 7 introduced Windows7IdxInfo.CreateIndex and Windows7IdxInfo.CreateIndex2 (and // Win8 has Windows8IdxInfo.CreateIndex3). Consider retrieving the conditional columns with that // API and converting the results. But that does not solve the problem for Vista. indexDefinition.conditionalColumnCollection = new ConditionalColumnCollection(); JET_TABLEID tableidCatalog; Api.JetOpenTable( database.IsamSession.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, tableName, Encoding.ASCII, MakeKeyGrbit.None); Api.JetSeek(sesid, tableidCatalog, SeekGrbit.SeekEQ); JET_COLUMNID columnidTemp = Api.GetTableColumnid(sesid, tableidCatalog, "ObjidTable"); int objidTable = Api.RetrieveColumnAsInt32(sesid, tableidCatalog, columnidTemp).GetValueOrDefault(); Api.JetSetCurrentIndex(sesid, tableidCatalog, "Name"); Api.MakeKey(sesid, tableidCatalog, objidTable, MakeKeyGrbit.NewKey); Api.MakeKey(sesid, tableidCatalog, (short)3, MakeKeyGrbit.None); Api.MakeKey(sesid, tableidCatalog, indexDefinition.name, Encoding.ASCII, MakeKeyGrbit.None); Api.JetSeek(sesid, tableidCatalog, SeekGrbit.SeekEQ); columnidTemp = Api.GetTableColumnid(sesid, tableidCatalog, "Flags"); int indexFlagsBytes = Api.RetrieveColumnAsInt32(sesid, tableidCatalog, columnidTemp).GetValueOrDefault(); columnidTemp = Api.GetTableColumnid(sesid, tableidCatalog, "ConditionalColumns"); byte[] conditionalColumnsBytes = Api.RetrieveColumn(sesid, tableidCatalog, columnidTemp); for (int ib = 0; conditionalColumnsBytes != null && ib < conditionalColumnsBytes.Length; ib += 4) { uint colid; bool mustBeNull; JET_COLUMNBASE columnBase; // fIDXExtendedColumns if ((indexFlagsBytes & 0xffff0000) == 0x00010000) { // fIDXSEGTemplateColumn if ((conditionalColumnsBytes[ib + 0] & 0x80) != 0) { // fCOLUMNIDTemplate colid = 0x80000000 | (uint)(conditionalColumnsBytes[ib + 3] << 8) | (uint)conditionalColumnsBytes[ib + 2]; } else { colid = (uint)(conditionalColumnsBytes[ib + 3] << 8) | (uint)conditionalColumnsBytes[ib + 2]; } // fIDXSEGMustBeNull if ((conditionalColumnsBytes[ib + 0] & 0x20) != 0) { mustBeNull = true; } else { mustBeNull = false; } } else { // do not load conditional columns from an unknown format continue; } JET_COLUMNID castedColid = JET_COLUMNID.CreateColumnidFromNativeValue(unchecked((int)colid)); VistaApi.JetGetColumnInfo( database.IsamSession.Sesid, database.Dbid, tableName, castedColid, out columnBase); indexDefinition.conditionalColumnCollection.Add(new ConditionalColumn(new Columnid(columnBase), mustBeNull)); } indexDefinition.conditionalColumnCollection.ReadOnly = true; indexDefinition.ReadOnly = true; return indexDefinition; } } }