Example #1
0
        /// <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);
        }
Example #2
0
        /// <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);
                }
            }
        }
Example #3
0
        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);
        }
Example #4
0
        public void ConvertRetinfoFromNative()
        {
            var native = new NATIVE_RETINFO { columnidNextTagged = 257 };

            var retinfo = new JET_RETINFO();
            retinfo.SetFromNativeRetinfo(native);

            Assert.AreEqual(257U, retinfo.columnidNextTagged.Value);
        }
Example #5
0
        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);
        }
Example #6
0
        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);
        }
Example #7
0
        /// <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);
                }
            }
        }
Example #8
0
        /// <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);
        }
Example #10
0
        /// <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);
                }
            }
        }
Example #11
0
        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);
        }
Example #12
0
        /// <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));
                }
            }
        }
Example #13
0
 /// <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)));
 }
Example #14
0
 public void JetRetinfoToString()
 {
     var value = new JET_RETINFO { ibLongValue = 1, itagSequence = 2 };
     Assert.AreEqual("JET_RETINFO(ibLongValue=1,itagSequence=2)", value.ToString());
 }
Example #15
0
        /// <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);
            }
        }
Example #16
0
        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);
        }
Example #18
0
 /// <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);
 }
Example #20
0
 /// <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);
        }
Example #22
0
        /// <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;
 }
Example #24
0
        /// <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);
                }
            }
        }
Example #25
0
        /// <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;
            }
        }