/// <summary> /// Deletes a single index from the table underlying this table /// </summary> /// <param name="name">The name.</param> /// <remarks> /// It is currently not possible to delete an index that is being used /// by a Cursor as its CurrentIndex. All such Cursors must either be /// disposed or set to a different index before the index can be /// successfully deleted. /// </remarks> public void DropIndex(string name) { lock (this.database.IsamSession) { using (IsamTransaction trx = new IsamTransaction(this.database.IsamSession)) { // open the table JET_TABLEID tableid; Api.JetOpenTable( this.database.IsamSession.Sesid, this.database.Dbid, this.name, null, 0, OpenTableGrbit.None, out tableid); // delete the index from the table Api.JetDeleteIndex(this.database.IsamSession.Sesid, tableid, name); // commit our change Api.JetCloseTable(this.database.IsamSession.Sesid, tableid); trx.Commit(); DatabaseCommon.SchemaUpdateID++; } } }
/// <summary> /// Creates a single column with the specified definition in the table /// underlying this table definition /// </summary> /// <param name="columnDefinition">The column definition.</param> /// <returns>The <see cref="Columnid"/> object corresponding to the /// newly-added column.</returns> /// <remarks> /// It is currently not possible to add an AutoIncrement column to a /// table that is being used by a Cursor. All such Cursors must be /// disposed before the column can be successfully added. /// </remarks> public Columnid AddColumn(ColumnDefinition columnDefinition) { lock (this.database.IsamSession) { using (IsamTransaction trx = new IsamTransaction(this.database.IsamSession)) { OpenTableGrbit grbit = OpenTableGrbit.None; // if we are trying to add an auto-inc column then we must // be able to open the table for exclusive access. if we can't // then we will not be able to add the column if ((columnDefinition.Flags & ColumnFlags.AutoIncrement) != 0) { grbit = grbit | OpenTableGrbit.DenyRead; } // open the table with the appropriate access JET_TABLEID tableid; Api.JetOpenTable(this.database.IsamSession.Sesid, this.database.Dbid, this.name, null, 0, grbit, out tableid); // add the new column to the table JET_COLUMNDEF columndef = new JET_COLUMNDEF(); columndef.coltyp = DatabaseCommon.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); int defaultValueBytesLength = (defaultValueBytes == null) ? 0 : defaultValueBytes.Length; JET_COLUMNID jetColumnid; Api.JetAddColumn( this.database.IsamSession.Sesid, tableid, columnDefinition.Name, columndef, defaultValueBytes, defaultValueBytesLength, out jetColumnid); // commit our change Api.JetCloseTable(this.database.IsamSession.Sesid, tableid); trx.Commit(); DatabaseCommon.SchemaUpdateID++; // return the columnid for the new column return(new Columnid( columnDefinition.Name, jetColumnid, columndef.coltyp, columndef.cp == JET_CP.ASCII)); } } }
/// <summary> /// Calculates the size of the specified multivalue stored in the specified column. /// </summary> /// <param name="columnName">Name of the column.</param> /// <param name="index">The index.</param> /// <returns>Returns the size of the specified multivalue stored in the specified column.</returns> public long SizeOf(string columnName, int index) { lock (this.isamSession) { this.cursor.CheckDisposed(); using (IsamTransaction trx = new IsamTransaction(this.isamSession, true)) { return(this.SizeOf(this.cursor.TableDefinition.Columns[columnName].Columnid, index)); } } }
/// <summary> /// Retrieves the column. /// </summary> /// <param name="columnDefinition">The column definition.</param> /// <param name="index">The index.</param> /// <returns>The value stored within.</returns> private object RetrieveColumn(ColumnDefinition columnDefinition, int index) { lock (this.isamSession) { this.cursor.CheckDisposed(); using (IsamTransaction trx = new IsamTransaction(this.isamSession, true)) { Columnid columnid = columnDefinition.Columnid; return(this.RetrieveColumn(columnid.InteropColumnid, columnid.Coltyp, columnid.IsAscii, index)); } } }
/// <summary> /// Sets the column. /// </summary> /// <param name="columnDefinition">The column definition.</param> /// <param name="index">The index.</param> /// <param name="obj">The object.</param> private void SetColumn(ColumnDefinition columnDefinition, int index, object obj) { lock (this.isamSession) { this.cursor.CheckDisposed(); using (IsamTransaction trx = new IsamTransaction(this.isamSession, true)) { Columnid columnid = columnDefinition.Columnid; this.SetColumn(columnid.InteropColumnid, columnid.Coltyp, columnid.IsAscii, index, obj); trx.Commit(); } } }
/// <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> /// Creates a single index with the specified definition in the table /// underlying this table definition /// </summary> /// <param name="indexDefinition">The index definition.</param> public void CreateIndex(IndexDefinition indexDefinition) { lock (this.database.IsamSession) { using (IsamTransaction trx = new IsamTransaction(this.database.IsamSession)) { // open the table JET_TABLEID tableid; Api.JetOpenTable( this.database.IsamSession.Sesid, this.database.Dbid, this.name, null, 0, OpenTableGrbit.None, out tableid); // add the new index to the table JET_INDEXCREATE[] indexcreates = new JET_INDEXCREATE[1] { new JET_INDEXCREATE() }; indexcreates[0].szIndexName = indexDefinition.Name; indexcreates[0].szKey = DatabaseCommon.IndexKeyFromIndexDefinition(indexDefinition); indexcreates[0].cbKey = indexcreates[0].szKey.Length; indexcreates[0].grbit = DatabaseCommon.GrbitFromIndexDefinition(indexDefinition); indexcreates[0].ulDensity = indexDefinition.Density; indexcreates[0].pidxUnicode = new JET_UNICODEINDEX(); indexcreates[0].pidxUnicode.lcid = indexDefinition.CultureInfo.LCID; indexcreates[0].pidxUnicode.dwMapFlags = Converter.MapFlagsFromUnicodeIndexFlags(Converter.UnicodeFlagsFromCompareOptions(indexDefinition.CompareOptions)); indexcreates[0].rgconditionalcolumn = DatabaseCommon.ConditionalColumnsFromIndexDefinition(indexDefinition); indexcreates[0].cConditionalColumn = indexcreates[0].rgconditionalcolumn.Length; indexcreates[0].cbKeyMost = indexDefinition.MaxKeyLength; Api.JetCreateIndex2(this.database.IsamSession.Sesid, tableid, indexcreates, indexcreates.Length); // commit our change Api.JetCloseTable(this.database.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 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); } } }
/// <summary> /// Retrieves the column. /// </summary> /// <param name="columnDefinition">The column definition.</param> /// <param name="index">The index.</param> /// <returns>The value stored within.</returns> private object RetrieveColumn(ColumnDefinition columnDefinition, int index) { lock (this.isamSession) { this.cursor.CheckDisposed(); using (IsamTransaction trx = new IsamTransaction(this.isamSession, true)) { Columnid columnid = columnDefinition.Columnid; return this.RetrieveColumn(columnid.InteropColumnid, columnid.Coltyp, columnid.IsAscii, index); } } }
/// <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 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 void CommitAttributeUpdate(DatastoreObject obj, string attributeName, IsamTransaction transaction, bool hasChanged, bool skipMetaUpdate) { if (hasChanged) { if (!skipMetaUpdate) { // Increment the current USN long currentUsn = ++this.context.DomainController.HighestCommittedUsn; DateTime now = DateTime.Now; obj.UpdateAttributeMeta(attributeName, currentUsn, now); } this.dataTableCursor.AcceptChanges(); transaction.Commit(); } else { // No changes have been made to the object this.dataTableCursor.RejectChanges(); transaction.Abort(); } }
/// <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> /// Gets the fields of the current row. /// </summary> /// <param name="grbit">The grbit.</param> /// <returns>A <see cref="FieldCollection"/> object to allow retrieval of all fields of the current row.</returns> internal FieldCollection GetFields(RetrieveColumnGrbit grbit) { JET_PFNREALLOC allocator = (context, pv, cb) => IntPtr.Zero == pv ? Marshal.AllocHGlobal(new IntPtr(cb)) : Marshal.ReAllocHGlobal(pv, new IntPtr(cb)); lock (this.isamSession) { this.CheckDisposed(); this.CheckRecord(); EnumerateColumnsGrbit enumerateGrbit = ((grbit & RetrieveColumnGrbit.RetrieveCopy) != 0) ? EnumerateColumnsGrbit.EnumerateCopy : EnumerateColumnsGrbit.None; using (IsamTransaction trx = new IsamTransaction(this.isamSession, true)) { // enumerate all field values in the current record or copy // buffer JET_ENUMCOLUMN[] jecs; int numColumnValues; Api.JetEnumerateColumns( this.isamSession.Sesid, this.tableid, 0, // numColumnids null, // columnids out numColumnValues, out jecs, allocator, IntPtr.Zero, // allocatorContext, int.MaxValue, enumerateGrbit); // create a field collection to contain our field values FieldCollection fields = new FieldCollection(); // save the location of the source record for these field values fields.Location = this.Location; // fill the field collection with our field values if (jecs != null && jecs.Length > 0) { foreach (JET_ENUMCOLUMN jec in jecs) { if (jec.rgEnumColumnValue != null && jec.rgEnumColumnValue.Length > 0) { JET_COLUMNBASE columnbase; VistaApi.JetGetTableColumnInfo( this.isamSession.Sesid, this.tableid, jec.columnid, out columnbase); Columnid columnid = new Columnid(columnbase); bool isAscii = columnbase.cp == JET_CP.ASCII; FieldValueCollection values = new FieldValueCollection(columnid); foreach (JET_ENUMCOLUMNVALUE jecv in jec.rgEnumColumnValue) { // FUTURE-2013/11/15-martinc: Drat, this is an IntPtr, and ObjectFromBytes() really // wants a byte[] array. Copying the data to a byte array just to simply // re-interpret it as an object is inefficient. // We should write Converter.ObjectFromIntPtr... byte[] bytesData = new byte[jecv.cbData]; Marshal.Copy(jecv.pvData, bytesData, 0, bytesData.Length); values.Add(Converter.ObjectFromBytes(columnbase.coltyp, isAscii, bytesData)); } values.ReadOnly = true; fields.Add(values); } } } fields.ReadOnly = true; // Return the field collection. return fields; } } }
/// <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> /// Calculates the size of the specified multivalue stored in the specified column. /// </summary> /// <param name="columnName">Name of the column.</param> /// <param name="index">The index.</param> /// <returns>Returns the size of the specified multivalue stored in the specified column.</returns> public long SizeOf(string columnName, int index) { lock (this.isamSession) { this.cursor.CheckDisposed(); using (IsamTransaction trx = new IsamTransaction(this.isamSession, true)) { return this.SizeOf(this.cursor.TableDefinition.Columns[columnName].Columnid, index); } } }