/// <summary> /// Reads a sequence of bytes from the current stream and advances the /// position within the stream by the number of bytes read. /// </summary> /// <param name="buffer">The buffer to read into.</param> /// <param name="offset">The offset in the buffer to read into.</param> /// <param name="count">The number of bytes to read.</param> /// <returns>The number of bytes read into the buffer.</returns> public override int Read(byte[] buffer, int offset, int count) { CheckBufferArguments(buffer, offset, count); if (this.ibLongValue >= this.Length) { return(0); } int bytesToRead = checked ((int)Math.Min(this.Length - this.ibLongValue, count)); int ignored; var retinfo = new JET_RETINFO { itagSequence = this.Itag, ibLongValue = this.ibLongValue }; Api.JetRetrieveColumn(this.sesid, this.tableid, this.columnid, buffer, bytesToRead, offset, out ignored, RetrieveGrbit, retinfo); checked { this.ibLongValue += bytesToRead; } return(bytesToRead); }
/// <summary> /// Retrieves a single column value from the current record. The record is that /// record associated with the index entry at the current position of the cursor. /// Alternatively, this function can retrieve a column from a record being created /// in the cursor copy buffer. This function can also retrieve column data from an /// index entry that references the current record. In addition to retrieving the /// actual column value, JetRetrieveColumn can also be used to retrieve the size /// of a column, before retrieving the column data itself so that application /// buffers can be sized appropriately. /// </summary> /// <remarks> /// This is an internal method that takes a buffer offset as well as size. /// </remarks> /// <param name="sesid">The session to use.</param> /// <param name="tableid">The cursor to retrieve the column from.</param> /// <param name="columnid">The columnid to retrieve.</param> /// <param name="data">The data buffer to be retrieved into.</param> /// <param name="dataSize">The size of the data buffer.</param> /// <param name="dataOffset">Offset into the data buffer to read data into.</param> /// <param name="actualDataSize">Returns the actual size of the data buffer.</param> /// <param name="grbit">Retrieve column options.</param> /// <param name="retinfo"> /// If pretinfo is give as NULL then the function behaves as though an itagSequence /// of 1 and an ibLongValue of 0 (zero) were given. This causes column retrieval to /// retrieve the first value of a multi-valued column, and to retrieve long data at /// offset 0 (zero). /// </param> /// <returns>An ESENT warning code.</returns> public static JET_wrn JetRetrieveColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, byte[] data, int dataSize, int dataOffset, out int actualDataSize, RetrieveColumnGrbit grbit, JET_RETINFO retinfo) { if (dataOffset < 0 || (null != data && 0 != dataSize && dataOffset >= data.Length) || (null == data && dataOffset != 0)) { throw new ArgumentOutOfRangeException( "dataOffset", dataOffset, "must be inside the data buffer"); } if ((null == data && dataSize > 0) || (null != data && dataSize > data.Length)) { throw new ArgumentOutOfRangeException( "dataSize", dataSize, "cannot be greater than the length of the data"); } unsafe { fixed (byte* pointer = data) { return Api.JetRetrieveColumn( sesid, tableid, columnid, new IntPtr(pointer + dataOffset), dataSize, out actualDataSize, grbit, retinfo); } } }
public void ConvertRetinfoToNative() { var retinfo = new JET_RETINFO { ibLongValue = 1, itagSequence = 2 }; NATIVE_RETINFO native = retinfo.GetNativeRetinfo(); Assert.AreEqual(1U, native.ibLongValue); Assert.AreEqual(2U, native.itagSequence); }
public void ConvertRetinfoFromNative() { var native = new NATIVE_RETINFO { columnidNextTagged = 257 }; var retinfo = new JET_RETINFO(); retinfo.SetFromNativeRetinfo(native); Assert.AreEqual(257U, retinfo.columnidNextTagged.Value); }
public void ConvertRetinfoToNative() { var retinfo = new JET_RETINFO(); retinfo.ibLongValue = 1; retinfo.itagSequence = 2; NATIVE_RETINFO native = retinfo.GetNativeRetinfo(); Assert.AreEqual<uint>(1, native.ibLongValue); Assert.AreEqual<uint>(2, native.itagSequence); }
public void ConvertRetinfo() { var retinfo = new JET_RETINFO { ibLongValue = 1, itagSequence = 2 }; NATIVE_RETINFO native = retinfo.GetNativeRetinfo(); native.columnidNextTagged = 300; retinfo.SetFromNativeRetinfo(native); Assert.AreEqual(1, retinfo.ibLongValue); Assert.AreEqual(2, retinfo.itagSequence); Assert.AreEqual(300U, retinfo.columnidNextTagged.Value); }
/// <summary> /// Retrieve the value for columns whose buffers were truncated. /// </summary> /// <param name="sesid">The session to use.</param> /// <param name="tableid">The table to use.</param> /// <param name="columnValues">The column values.</param> /// <param name="nativeRetrievecolumns"> /// The native retrieve columns that match the column values. /// </param> private static unsafe void RetrieveTruncatedBuffers(JET_SESID sesid, JET_TABLEID tableid, IList <ColumnValue> columnValues, NATIVE_RETRIEVECOLUMN *nativeRetrievecolumns) { for (int i = 0; i < columnValues.Count; ++i) { if (nativeRetrievecolumns[i].err == (int)JET_wrn.BufferTruncated) { var buffer = new byte[nativeRetrievecolumns[i].cbActual]; int actualSize; int err; var retinfo = new JET_RETINFO { itagSequence = columnValues[i].ItagSequence }; // Pin the buffer and retrieve the data fixed(byte *pinnedBuffer = buffer) { err = Api.Impl.JetRetrieveColumn( sesid, tableid, columnValues[i].Columnid, new IntPtr(pinnedBuffer), buffer.Length, out actualSize, columnValues[i].RetrieveGrbit, retinfo); } if (JET_wrn.BufferTruncated == (JET_wrn)err) { string error = string.Format( CultureInfo.CurrentCulture, "Column size changed from {0} to {1}. The record was probably updated by another thread.", buffer.Length, actualSize); Trace.TraceError(error); throw new InvalidOperationException(error); } // Throw errors, but put warnings in the structure Api.Check(err); columnValues[i].Error = (JET_wrn)err; // For BytesColumnValue this will copy the data to a new array. // If this situation becomes common we should simply use the array. columnValues[i].GetValueFromBytes(buffer, 0, actualSize, err); } } }
/// <summary> /// Sets the length of the stream. /// </summary> /// <param name="value">The desired length, in bytes.</param> public override void SetLength(long value) { if (value > MaxLongValueSize || value < 0) { throw new ArgumentOutOfRangeException("value", value, "A LongValueStream cannot be longer than 0x7FFFFFF or less than 0 bytes"); } if (value < this.Length && value > 0) { // BUG: Shrinking the column multiple times and then growing it can sometimes hit an unpleasant // ESENT defect which causes a hang. To make sure we never have that problem we read out the data, // and insert into a new long-value. This is not efficient. var data = new byte[value]; var retinfo = new JET_RETINFO { itagSequence = this.Itag, ibLongValue = 0 }; int actualDataSize; Api.JetRetrieveColumn( this.sesid, this.tableid, this.columnid, data, data.Length, out actualDataSize, RetrieveGrbit, retinfo); var setinfo = new JET_SETINFO { itagSequence = this.Itag }; Api.JetSetColumn(this.sesid, this.tableid, this.columnid, data, data.Length, SetColumnGrbit.None, setinfo); } else { var setinfo = new JET_SETINFO { itagSequence = this.Itag }; SetColumnGrbit grbit = (0 == value) ? SetColumnGrbit.ZeroLength : SetColumnGrbit.SizeLV; Api.JetSetColumn(this.sesid, this.tableid, this.columnid, null, checked ((int)value), grbit, setinfo); } // Setting the length moves the offset back to the end of the data if (this.ibLongValue > value) { this.ibLongValue = checked ((int)value); } }
/// <summary> /// Retrieves the size of a single column value from the current record. /// The record is that record associated with the index entry at the /// current position of the cursor. Alternatively, this function can /// retrieve a column from a record being created in the cursor copy /// buffer. This function can also retrieve column data from an index /// entry that references the current record. /// </summary> /// <param name="sesid">The session to use.</param> /// <param name="tableid">The cursor to retrieve the column from.</param> /// <param name="columnid">The columnid to retrieve.</param> /// <param name="itagSequence"> /// The sequence number of value in a multi-valued column. /// The array of values is one-based. The first value is /// sequence 1, not 0. If the record column has only one value then /// 1 should be passed as the itagSequence. /// </param> /// <param name="grbit">Retrieve column options.</param> /// <returns>The size of the column. 0 if the column is null.</returns> public static int?RetrieveColumnSize( JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, int itagSequence, RetrieveColumnGrbit grbit) { var retinfo = new JET_RETINFO { itagSequence = itagSequence }; int dataSize; JET_wrn wrn = JetRetrieveColumn( sesid, tableid, columnid, null, 0, out dataSize, grbit, retinfo); if (JET_wrn.ColumnNull == wrn) { return(null); } return(dataSize); }
/// <summary> /// Retrieve the value for columns whose buffers were truncated. /// </summary> /// <param name="sesid">The session to use.</param> /// <param name="tableid">The table to use.</param> /// <param name="columnValues">The column values.</param> /// <param name="nativeRetrievecolumns"> /// The native retrieve columns that match the column values. /// </param> private static unsafe void RetrieveTruncatedBuffers(JET_SESID sesid, JET_TABLEID tableid, ColumnValue[] columnValues, NATIVE_RETRIEVECOLUMN *nativeRetrievecolumns) { for (int i = 0; i < columnValues.Length; ++i) { if (nativeRetrievecolumns[i].err == (int)JET_wrn.BufferTruncated) { var buffer = new byte[nativeRetrievecolumns[i].cbActual]; int actualSize; int err; var retinfo = new JET_RETINFO { itagSequence = columnValues[i].ItagSequence }; // Pin the buffer and retrieve the data fixed(byte *pinnedBuffer = buffer) { err = Api.Impl.JetRetrieveColumn( sesid, tableid, columnValues[i].Columnid, new IntPtr(pinnedBuffer), buffer.Length, out actualSize, columnValues[i].RetrieveGrbit, retinfo); } // Set the error in the ColumnValue before checkin it columnValues[i].Error = (JET_err)err; Api.Check(err); // For BytesColumnValue this will copy the data to a new array. // If this situation becomes common we should simply use the array. columnValues[i].GetValueFromBytes(buffer, 0, actualSize, err); } } }
public void HowDoIDealWithMultivalues() { JET_SESID sesid = this.testSession; JET_DBID dbid = this.testDbid; JET_TABLEID tableid; JET_COLUMNDEF columndef = new JET_COLUMNDEF(); JET_COLUMNID tagColumn; Api.JetCreateTable(sesid, dbid, "table", 0, 100, out tableid); // Create the column. Any column can be multivalued. Using // ColumndefGrbit controls how the column is indexed. columndef.coltyp = JET_coltyp.LongText; columndef.cp = JET_CP.Unicode; columndef.grbit = ColumndefGrbit.ColumnMultiValued; Api.JetAddColumn(sesid, tableid, "tags", columndef, null, 0, out tagColumn); // Create the index. There will be one entry in the index for each // instance of the multivalued column. const string IndexKey = "+tags\0\0"; Api.JetCreateIndex(sesid, tableid, "tagsindex", CreateIndexGrbit.None, IndexKey, IndexKey.Length, 100); Api.JetBeginTransaction(sesid); // Now insert a record. An ESENT column can have multiple instances (multivalues) // inside the same record. Each multivalue is identified by an itag, the first itag // in a sequence is 1. Api.JetPrepareUpdate(sesid, tableid, JET_prep.Insert); // With no JET_SETINFO specified, itag 1 will be set. byte[] data = Encoding.Unicode.GetBytes("foo"); Api.JetSetColumn(sesid, tableid, tagColumn, data, data.Length, SetColumnGrbit.None, null); // Set a column with a setinfo. The itagSequence in the setinfo will be 0, which // means the value will be added to the collection of values, i.e. the column will // have two instances, "foo" and "bar". JET_SETINFO setinfo = new JET_SETINFO(); data = Encoding.Unicode.GetBytes("bar"); Api.JetSetColumn(sesid, tableid, tagColumn, data, data.Length, SetColumnGrbit.None, setinfo); // Add a third instance, explicitly setting the itagSequence data = Encoding.Unicode.GetBytes("baz"); setinfo.itagSequence = 4; Api.JetSetColumn(sesid, tableid, tagColumn, data, data.Length, SetColumnGrbit.None, setinfo); // Add a duplicate value, checking for uniqueness data = Encoding.Unicode.GetBytes("foo"); setinfo.itagSequence = 0; try { Api.JetSetColumn(sesid, tableid, tagColumn, data, data.Length, SetColumnGrbit.UniqueMultiValues, setinfo); Assert.Fail("Expected an EsentErrorException"); } catch (EsentErrorException ex) { Assert.AreEqual(JET_err.MultiValuedDuplicate, ex.Error); } Api.JetUpdate(sesid, tableid); // Find the record. We can seek for any column instance. Api.JetSetCurrentIndex(sesid, tableid, "tagsindex"); Api.MakeKey(sesid, tableid, "bar", Encoding.Unicode, MakeKeyGrbit.NewKey); Assert.IsTrue(Api.TrySeek(sesid, tableid, SeekGrbit.SeekEQ)); // Retrieve the number of column instances. This can be done with JetRetrieveColumns by setting // itagSequence to 0. JET_RETRIEVECOLUMN retrievecolumn = new JET_RETRIEVECOLUMN(); retrievecolumn.columnid = tagColumn; retrievecolumn.itagSequence = 0; Api.JetRetrieveColumns(sesid, tableid, new[] { retrievecolumn }, 1); Console.WriteLine("{0}", retrievecolumn.itagSequence); Assert.AreEqual(3, retrievecolumn.itagSequence); // Retrieve all the columns for (int itag = 1; itag <= retrievecolumn.itagSequence; ++itag) { JET_RETINFO retinfo = new JET_RETINFO { itagSequence = itag }; string s = Encoding.Unicode.GetString(Api.RetrieveColumn(sesid, tableid, tagColumn, RetrieveColumnGrbit.None, retinfo)); Console.WriteLine("{0}: {1}", itag, s); } // Update the record Api.JetPrepareUpdate(sesid, tableid, JET_prep.Replace); // With no JET_SETINFO specified, itag 1 will be set, overwriting the existing value. data = Encoding.Unicode.GetBytes("qux"); Api.JetSetColumn(sesid, tableid, tagColumn, data, data.Length, SetColumnGrbit.None, null); // Set an instance to null to delete it. setinfo.itagSequence = 2; Api.JetSetColumn(sesid, tableid, tagColumn, null, 0, SetColumnGrbit.None, setinfo); // Removing itag 2 moved the other itags down (i.e. itag 3 became itag 2). // Overwrite itag 2. data = Encoding.Unicode.GetBytes("xyzzy"); setinfo.itagSequence = 2; Api.JetSetColumn(sesid, tableid, tagColumn, data, data.Length, SetColumnGrbit.None, setinfo); // Now add a new instance by setting itag 0. This instance will go at the end. data = Encoding.Unicode.GetBytes("flob"); setinfo.itagSequence = 0; Api.JetSetColumn(sesid, tableid, tagColumn, data, data.Length, SetColumnGrbit.None, setinfo); Api.JetUpdate(sesid, tableid); // Retrieve the number of column instances again. retrievecolumn.itagSequence = 0; Api.JetRetrieveColumns(sesid, tableid, new[] { retrievecolumn }, 1); // Retrieve all the columns for (int itag = 1; itag <= retrievecolumn.itagSequence; ++itag) { JET_RETINFO retinfo = new JET_RETINFO { itagSequence = itag }; string s = Encoding.Unicode.GetString(Api.RetrieveColumn(sesid, tableid, tagColumn, RetrieveColumnGrbit.None, retinfo)); Console.WriteLine("{0}: {1}", itag, s); } Api.JetCommitTransaction(sesid, CommitTransactionGrbit.LazyFlush); }
/// <summary> /// Retrieves a single column value from the current record. The record is that /// record associated with the index entry at the current position of the cursor. /// Alternatively, this function can retrieve a column from a record being created /// in the cursor copy buffer. This function can also retrieve column data from an /// index entry that references the current record. In addition to retrieving the /// actual column value, JetRetrieveColumn can also be used to retrieve the size /// of a column, before retrieving the column data itself so that application /// buffers can be sized appropriately. /// </summary> /// <remarks> /// This is an internal method that takes a buffer offset as well as size. /// </remarks> /// <param name="sesid">The session to use.</param> /// <param name="tableid">The cursor to retrieve the column from.</param> /// <param name="columnid">The columnid to retrieve.</param> /// <param name="data">The data buffer to be retrieved into.</param> /// <param name="dataSize">The size of the data buffer.</param> /// <param name="dataOffset">Offset into the data buffer to read data into.</param> /// <param name="actualDataSize">Returns the actual size of the data buffer.</param> /// <param name="grbit">Retrieve column options.</param> /// <param name="retinfo"> /// If pretinfo is give as NULL then the function behaves as though an itagSequence /// of 1 and an ibLongValue of 0 (zero) were given. This causes column retrieval to /// retrieve the first value of a multi-valued column, and to retrieve long data at /// offset 0 (zero). /// </param> /// <returns>An ESENT warning code.</returns> public static JET_wrn JetRetrieveColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, byte[] data, int dataSize, int dataOffset, out int actualDataSize, RetrieveColumnGrbit grbit, JET_RETINFO retinfo) { if (dataOffset < 0 || (null != data && 0 != dataSize && dataOffset >= data.Length) || (null == data && dataOffset != 0)) { throw new ArgumentOutOfRangeException( "dataOffset", dataOffset, "must be inside the data buffer"); } if ((null == data && dataSize > 0) || (null != data && dataSize > data.Length)) { throw new ArgumentOutOfRangeException( "dataSize", dataSize, "cannot be greater than the length of the data"); } unsafe { fixed(byte *pointer = data) { return(Api.JetRetrieveColumn( sesid, tableid, columnid, new IntPtr(pointer + dataOffset), dataSize, out actualDataSize, grbit, retinfo)); } } }
/// <summary> /// Retrieves a single column value from the current record. The record is that /// record associated with the index entry at the current position of the cursor. /// Alternatively, this function can retrieve a column from a record being created /// in the cursor copy buffer. This function can also retrieve column data from an /// index entry that references the current record. In addition to retrieving the /// actual column value, JetRetrieveColumn can also be used to retrieve the size /// of a column, before retrieving the column data itself so that application /// buffers can be sized appropriately. /// </summary> /// <remarks> /// This is an internal-use version that takes an IntPtr. /// </remarks> /// <param name="sesid">The session to use.</param> /// <param name="tableid">The cursor to retrieve the column from.</param> /// <param name="columnid">The columnid to retrieve.</param> /// <param name="data">The data buffer to be retrieved into.</param> /// <param name="dataSize">The size of the data buffer.</param> /// <param name="actualDataSize">Returns the actual size of the data buffer.</param> /// <param name="grbit">Retrieve column options.</param> /// <param name="retinfo"> /// If pretinfo is give as NULL then the function behaves as though an itagSequence /// of 1 and an ibLongValue of 0 (zero) were given. This causes column retrieval to /// retrieve the first value of a multi-valued column, and to retrieve long data at /// offset 0 (zero). /// </param> /// <returns>An ESENT warning code.</returns> internal static JET_wrn JetRetrieveColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, IntPtr data, int dataSize, out int actualDataSize, RetrieveColumnGrbit grbit, JET_RETINFO retinfo) { return(Api.Check(Impl.JetRetrieveColumn(sesid, tableid, columnid, data, dataSize, out actualDataSize, grbit, retinfo))); }
public void JetRetinfoToString() { var value = new JET_RETINFO { ibLongValue = 1, itagSequence = 2 }; Assert.AreEqual("JET_RETINFO(ibLongValue=1,itagSequence=2)", value.ToString()); }
/// <summary> /// Sets the length of the stream. /// </summary> /// <param name="value">The desired length, in bytes.</param> public override void SetLength(long value) { if (value > MaxLongValueSize || value < 0) { throw new ArgumentOutOfRangeException("value", value, "A LongValueStream cannot be longer than 0x7FFFFFF or less than 0 bytes"); } if (value < this.Length && value > 0) { // BUG: Shrinking the column multiple times and then growing it can sometimes hit an unpleasant // ESENT defect which causes a hang. To make sure we never have that problem we read out the data, // and insert into a new long-value. This is not efficient. var data = new byte[value]; var retinfo = new JET_RETINFO { itagSequence = this.Itag, ibLongValue = 0 }; int actualDataSize; Api.JetRetrieveColumn( this.sesid, this.tableid, this.columnid, data, data.Length, out actualDataSize, RetrieveGrbit, retinfo); var setinfo = new JET_SETINFO { itagSequence = this.Itag }; Api.JetSetColumn(this.sesid, this.tableid, this.columnid, data, data.Length, SetColumnGrbit.None, setinfo); } else { var setinfo = new JET_SETINFO { itagSequence = this.Itag }; SetColumnGrbit grbit = (0 == value) ? SetColumnGrbit.ZeroLength : SetColumnGrbit.SizeLV; Api.JetSetColumn(this.sesid, this.tableid, this.columnid, null, checked((int)value), grbit, setinfo); } // Setting the length moves the offset back to the end of the data if (this.ibLongValue > value) { this.ibLongValue = checked((int)value); } }
public void ConvertRetinfoToNativeWhenItagSequenceIsNegative() { var retinfo = new JET_RETINFO { ibLongValue = 0, itagSequence = Int32.MinValue }; NATIVE_RETINFO native = retinfo.GetNativeRetinfo(); }
/// <summary> /// Retrieves a single column value from the current record. The record is that /// record associated with the index entry at the current position of the cursor. /// Alternatively, this function can retrieve a column from a record being created /// in the cursor copy buffer. This function can also retrieve column data from an /// index entry that references the current record. In addition to retrieving the /// actual column value, JetRetrieveColumn can also be used to retrieve the size /// of a column, before retrieving the column data itself so that application /// buffers can be sized appropriately. /// </summary> /// <param name="sesid">The session to use.</param> /// <param name="tableid">The cursor to retrieve the column from.</param> /// <param name="columnid">The columnid to retrieve.</param> /// <param name="grbit">Retrieve column options.</param> /// <param name="retinfo"> /// If pretinfo is give as NULL then the function behaves as though an itagSequence /// of 1 and an ibLongValue of 0 (zero) were given. This causes column retrieval to /// retrieve the first value of a multi-valued column, and to retrieve long data at /// offset 0 (zero). /// </param> /// <returns>The data retrieved from the column. Null if the column is null.</returns> public static byte[] RetrieveColumn( JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit, JET_RETINFO retinfo) { // We expect most column values retrieved this way to be small (retrieving a 1GB LV as one // chunk is a bit extreme!). Allocate a cached buffer and use that, allocating a larger one // if needed. byte[] cache = Caches.ColumnCache.Allocate(); byte[] data = cache; int dataSize; JET_wrn wrn = JetRetrieveColumn( sesid, tableid, columnid, data, data.Length, out dataSize, grbit, retinfo); if (JET_wrn.ColumnNull == wrn) { // null column data = null; } else if (JET_wrn.Success == wrn) { data = MemoryCache.Duplicate(data, dataSize); } else { // there is more data to retrieve Debug.Assert(JET_wrn.BufferTruncated == wrn, "Unexpected warning", wrn.ToString()); data = new byte[dataSize]; wrn = JetRetrieveColumn( sesid, tableid, columnid, data, data.Length, out dataSize, grbit, retinfo); if (JET_wrn.Success != wrn) { string error = String.Format( CultureInfo.CurrentCulture, "Column size changed from {0} to {1}. The record was probably updated by another thread.", data.Length, dataSize); Trace.TraceError(error); throw new InvalidOperationException(error); } } Caches.ColumnCache.Free(ref cache); return(data); }
/// <summary> /// Retrieves a single column value from the current record. The record is that /// record associated with the index entry at the current position of the cursor. /// Alternatively, this function can retrieve a column from a record being created /// in the cursor copy buffer. This function can also retrieve column data from an /// index entry that references the current record. In addition to retrieving the /// actual column value, JetRetrieveColumn can also be used to retrieve the size /// of a column, before retrieving the column data itself so that application /// buffers can be sized appropriately. /// </summary> /// <remarks> /// The RetrieveColumnAs functions provide datatype-specific retrieval functions. /// </remarks> /// <param name="sesid">The session to use.</param> /// <param name="tableid">The cursor to retrieve the column from.</param> /// <param name="columnid">The columnid to retrieve.</param> /// <param name="data">The data buffer to be retrieved into.</param> /// <param name="dataSize">The size of the data buffer.</param> /// <param name="actualDataSize">Returns the actual size of the data buffer.</param> /// <param name="grbit">Retrieve column options.</param> /// <param name="retinfo"> /// If pretinfo is give as NULL then the function behaves as though an itagSequence /// of 1 and an ibLongValue of 0 (zero) were given. This causes column retrieval to /// retrieve the first value of a multi-valued column, and to retrieve long data at /// offset 0 (zero). /// </param> /// <returns>An ESENT warning code.</returns> public static JET_wrn JetRetrieveColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, byte[] data, int dataSize, out int actualDataSize, RetrieveColumnGrbit grbit, JET_RETINFO retinfo) { return Api.JetRetrieveColumn(sesid, tableid, columnid, data, dataSize, 0, out actualDataSize, grbit, retinfo); }
public void JetRetrieveColumnThrowsExceptionWhenIbLongValueIsNegative() { int actualSize; var retinfo = new JET_RETINFO { ibLongValue = -1 }; Api.JetRetrieveColumn(this.sesid, this.tableid, this.columnid, null, 0, out actualSize, RetrieveColumnGrbit.None, retinfo); }
/// <summary> /// Retrieves a single column value from the current record. The record is that /// record associated with the index entry at the current position of the cursor. /// Alternatively, this function can retrieve a column from a record being created /// in the cursor copy buffer. This function can also retrieve column data from an /// index entry that references the current record. In addition to retrieving the /// actual column value, JetRetrieveColumn can also be used to retrieve the size /// of a column, before retrieving the column data itself so that application /// buffers can be sized appropriately. /// </summary> /// <remarks> /// This is an internal-use version that takes an IntPtr. /// </remarks> /// <param name="sesid">The session to use.</param> /// <param name="tableid">The cursor to retrieve the column from.</param> /// <param name="columnid">The columnid to retrieve.</param> /// <param name="data">The data buffer to be retrieved into.</param> /// <param name="dataSize">The size of the data buffer.</param> /// <param name="actualDataSize">Returns the actual size of the data buffer.</param> /// <param name="grbit">Retrieve column options.</param> /// <param name="retinfo"> /// If pretinfo is give as NULL then the function behaves as though an itagSequence /// of 1 and an ibLongValue of 0 (zero) were given. This causes column retrieval to /// retrieve the first value of a multi-valued column, and to retrieve long data at /// offset 0 (zero). /// </param> /// <returns>An ESENT warning code.</returns> internal static JET_wrn JetRetrieveColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, IntPtr data, int dataSize, out int actualDataSize, RetrieveColumnGrbit grbit, JET_RETINFO retinfo) { return Api.Check(Impl.JetRetrieveColumn(sesid, tableid, columnid, data, dataSize, out actualDataSize, grbit, retinfo)); }
/// <summary> /// Retrieves a single column value from the current record. The record is that /// record associated with the index entry at the current position of the cursor. /// Alternatively, this function can retrieve a column from a record being created /// in the cursor copy buffer. This function can also retrieve column data from an /// index entry that references the current record. In addition to retrieving the /// actual column value, JetRetrieveColumn can also be used to retrieve the size /// of a column, before retrieving the column data itself so that application /// buffers can be sized appropriately. /// </summary> /// <param name="sesid">The session to use.</param> /// <param name="tableid">The cursor to retrieve the column from.</param> /// <param name="columnid">The columnid to retrieve.</param> /// <param name="grbit">Retrieve column options.</param> /// <param name="retinfo"> /// If pretinfo is give as NULL then the function behaves as though an itagSequence /// of 1 and an ibLongValue of 0 (zero) were given. This causes column retrieval to /// retrieve the first value of a multi-valued column, and to retrieve long data at /// offset 0 (zero). /// </param> /// <returns>The data retrieved from the column. Null if the column is null.</returns> public static byte[] RetrieveColumn( JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit, JET_RETINFO retinfo) { // We expect most column values retrieved this way to be small (retrieving a 1GB LV as one // chunk is a bit extreme!). Allocate a small buffer and use that, allocating a larger one // if needed. var data = new byte[256]; int dataSize; JET_wrn wrn = JetRetrieveColumn( sesid, tableid, columnid, data, data.Length, out dataSize, grbit, retinfo); if (JET_wrn.ColumnNull == wrn) { // null column data = null; } else { Array.Resize(ref data, dataSize); if (JET_wrn.BufferTruncated == wrn) { // there is more data to retrieve wrn = JetRetrieveColumn( sesid, tableid, columnid, data, data.Length, out dataSize, grbit, retinfo); if (JET_wrn.Success != wrn) { string error = String.Format( CultureInfo.CurrentCulture, "Column size changed from {0} to {1}. The record was probably updated by another thread.", data.Length, dataSize); Trace.TraceError(error); throw new InvalidOperationException(error); } } } return(data); }
/// <summary> /// Reads a sequence of bytes from the current stream and advances the /// position within the stream by the number of bytes read. /// </summary> /// <param name="buffer">The buffer to read into.</param> /// <param name="offset">The offset in the buffer to read into.</param> /// <param name="count">The number of bytes to read.</param> /// <returns>The number of bytes read into the buffer.</returns> public override int Read(byte[] buffer, int offset, int count) { CheckBufferArguments(buffer, offset, count); if (this.ibLongValue >= this.Length) { return 0; } int length; var retinfo = new JET_RETINFO { itagSequence = this.Itag, ibLongValue = this.ibLongValue }; Api.JetRetrieveColumn(this.sesid, this.tableid, this.columnid, buffer, count, offset, out length, RetrieveGrbit, retinfo); int bytesRead = Math.Min(length, count); checked { this.ibLongValue += bytesRead; } return bytesRead; }
/// <summary> /// A retrieve column function which always claims that the data in the column /// is larger than the passed in buffer. /// </summary> /// <param name="sesid">The session to use.</param> /// <param name="tableid">The cursor to retrieve the column from.</param> /// <param name="columnid">The columnid to retrieve.</param> /// <param name="data">The data buffer to be retrieved into.</param> /// <param name="dataSize">The size of the data buffer.</param> /// <param name="actualDataSize">Returns the actual size of the data buffer.</param> /// <param name="grbit">Retrieve column options.</param> /// <param name="retinfo"> /// Retrieve options. /// </param> /// <returns><see cref="JET_wrn.BufferTruncated"/>.</returns> private static int BadRetrieveColumn( JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, IntPtr data, int dataSize, out int actualDataSize, RetrieveColumnGrbit grbit, JET_RETINFO retinfo) { actualDataSize = (dataSize * 2) + (1024 * 1024); return (int)JET_wrn.BufferTruncated; }
/// <summary> /// Retrieve the value for columns whose buffers were truncated. /// </summary> /// <param name="sesid">The session to use.</param> /// <param name="tableid">The table to use.</param> /// <param name="columnValues">The column values.</param> /// <param name="nativeRetrievecolumns"> /// The native retrieve columns that match the column values. /// </param> private static unsafe void RetrieveTruncatedBuffers(JET_SESID sesid, JET_TABLEID tableid, IList<ColumnValue> columnValues, NATIVE_RETRIEVECOLUMN* nativeRetrievecolumns) { for (int i = 0; i < columnValues.Count; ++i) { if (nativeRetrievecolumns[i].err == (int)JET_wrn.BufferTruncated) { var buffer = new byte[nativeRetrievecolumns[i].cbActual]; int actualSize; int err; var retinfo = new JET_RETINFO { itagSequence = columnValues[i].ItagSequence }; // Pin the buffer and retrieve the data fixed (byte* pinnedBuffer = buffer) { err = Api.Impl.JetRetrieveColumn( sesid, tableid, columnValues[i].Columnid, new IntPtr(pinnedBuffer), buffer.Length, out actualSize, columnValues[i].RetrieveGrbit, retinfo); } // Throw errors, but put warnings in the structure Api.Check(err); columnValues[i].Error = (JET_wrn)err; // For BytesColumnValue this will copy the data to a new array. // If this situation becomes common we should simply use the array. columnValues[i].GetValueFromBytes(buffer, 0, actualSize, err); } } }
/// <summary> /// Retrieves the column. /// </summary> /// <param name="columnid">The columnid.</param> /// <param name="coltyp">The coltyp.</param> /// <param name="isAscii">Whether a textual column is Ascii.</param> /// <param name="index">The index.</param> /// <returns>The value stored within.</returns> /// <remarks> /// Itags start at 1, so we add 1 to the index /// </remarks> private object RetrieveColumn(JET_COLUMNID columnid, JET_coltyp coltyp, bool isAscii, int index) { lock (this.isamSession) { this.cursor.CheckDisposed(); if ((this.grbit & RetrieveColumnGrbit.RetrieveCopy) == 0) { this.cursor.CheckRecord(); } JET_RETINFO retinfo = new JET_RETINFO(); retinfo.ibLongValue = 0; retinfo.itagSequence = index + 1; byte[] bytes = Api.RetrieveColumn(this.isamSession.Sesid, this.tableid, columnid, this.grbit, retinfo); object obj = Converter.ObjectFromBytes(coltyp, isAscii, bytes); return obj; } }