/// <summary> /// Set the current index of the cursor /// </summary> /// <param name="indexName">The name of the index on the table for this cursor</param> /// <remarks> /// This will affect the order records are traversed by the MoveNext, /// MovePrevious and Move methods. The cursor will be positioned before /// the first record on the index. /// <para> /// As a side effect, any restriction in effect will be cleared. /// </para> /// </remarks> public void SetCurrentIndex(string indexName) { lock (this.isamSession) { this.CheckDisposed(); // don't do anything if we are already on the new index if (string.Compare(indexName, this.CurrentIndex, StringComparison.OrdinalIgnoreCase) != 0) { // if this is a TT then selecting another index will fail if (this.isTempTable) { throw new EsentIndexNotFoundException(); } this.OnNavigation(); // select the new index Api.JetSetCurrentIndex(this.isamSession.Sesid, this.tableid, indexName); // purge our cached index definition this.indexDefinition = null; // clear our field cache because the current record has changed this.fields = null; // clear our index range this.FindAllRecords(); // move before first on the new index this.MoveBeforeFirst(); } } }
/// <summary> /// Set the current index of the cursor /// </summary> /// <param name="indexName">The name of the index on the table for this cursor</param> /// <remarks> /// This will affect the order records are traversed by the MoveNext, /// MovePrevious and Move methods. The cursor will be positioned on /// the new index at the first entry corresponding to the same record /// for the entry in the old index. If no such entry exists because /// that record does not have an entry in the new index then the /// operation will fail with EsentNoCurrentRecordException. /// <para> /// As a side effect, any restriction in effect will be cleared. /// </para> /// </remarks> public void MoveToIndex(string indexName) { lock (this.isamSession) { this.CheckDisposed(); // don't do anything if we are already on the new index if (string.Compare(indexName, this.CurrentIndex, StringComparison.OrdinalIgnoreCase) != 0) { // if this is a TT then selecting another index will fail if (this.isTempTable) { throw new EsentIndexNotFoundException(); } // select the new index and attempt to maintain our // position on this record Api.JetSetCurrentIndex2(this.isamSession.Sesid, this.tableid, indexName, SetCurrentIndexGrbit.NoMove); // purge our cached index definition this.indexDefinition = null; // clear our index range this.FindAllRecords(); } } }
/// <summary> /// Advances the enumerator to the next element of the collection. /// </summary> /// <returns> /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. /// </returns> public bool MoveNext() { if (this.database != null) { lock (this.database.IsamSession) { if (!this.moved) { Api.JetGetIndexInfo( this.database.IsamSession.Sesid, this.database.Dbid, this.tableName, null, out this.indexList, JET_IdxInfo.List); this.cleanup = true; this.current = Api.TryMoveFirst(this.database.IsamSession.Sesid, this.indexList.tableid); } else if (this.current) { this.current = Api.TryMoveNext(this.database.IsamSession.Sesid, this.indexList.tableid); if (!this.current) { Api.JetCloseTable(this.database.IsamSession.Sesid, this.indexList.tableid); this.cleanup = false; } } this.moved = true; this.indexDefinition = null; return this.current; } } else { return this.enumerator.MoveNext(); } }
/// <summary> /// Sets the enumerator to its initial position, which is before the first element in the collection. /// </summary> public void Reset() { if (this.database != null) { lock (this.database.IsamSession) { if (this.cleanup) { if (!this.database.IsamSession.Disposed) { // BUGBUG: we will try to close an already closed tableid // if it was already closed due to a rollback. this could // cause us to crash in ESENT due to lack of full validation // in small config. we should use cursor LS to detect when // our cursor gets closed and thus avoid closing it again Api.JetCloseTable(this.database.IsamSession.Sesid, this.indexList.tableid); } } this.cleanup = false; this.moved = false; this.current = false; this.indexDefinition = null; } } else { this.enumerator.Reset(); } }
/// <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; } } }