/// <summary>
        /// Recursively pin the retrieve buffers in the JET_RETRIEVECOLUMN
        /// structures and then retrieve the columns. This is done to avoid
        /// creating GCHandles which is very expensive. This function pins
        /// the current retrievecolumn structure (indicated by i) and then
        /// recursively calls itself until all structures are pinned. This
        /// is done because it isn't possible to create an arbitrary number
        /// of pinned variables in a method.
        /// </summary>
        /// <param name="sesid">
        /// The session to use.
        /// </param>
        /// <param name="tableid">
        /// The table to retrieve from.
        /// </param>
        /// <param name="nativeretrievecolumns">
        /// The nativeretrievecolumns structure.</param>
        /// <param name="retrievecolumns">
        /// The managed retrieve columns structure.
        /// </param>
        /// <param name="numColumns">The number of columns.</param>
        /// <param name="i">The column currently being processed.</param>
        /// <returns>An error code from JetRetrieveColumns.</returns>
        private static unsafe int PinColumnsAndRetrieve(
            JET_SESID sesid,
            JET_TABLEID tableid,
            NATIVE_RETRIEVECOLUMN *nativeretrievecolumns,
            JET_RETRIEVECOLUMN[] retrievecolumns,
            int numColumns,
            int i)
        {
            retrievecolumns[i].CheckDataSize();
            nativeretrievecolumns[i] = retrievecolumns[i].GetNativeRetrievecolumn();
            fixed(byte *pinnedBuffer = retrievecolumns[i].pvData)
            {
                nativeretrievecolumns[i].pvData = new IntPtr(pinnedBuffer);

                if (numColumns - 1 == i)
                {
                    return(Impl.JetRetrieveColumns(sesid, tableid, nativeretrievecolumns, numColumns));
                }

                return(PinColumnsAndRetrieve(sesid, tableid, nativeretrievecolumns, retrievecolumns, numColumns, i + 1));
            }
        }
        /// <summary>
        /// Recursively pin the retrieve buffers in the JET_RETRIEVECOLUMN
        /// structures and then retrieve the columns. This is done to avoid
        /// creating GCHandles, which are expensive. This function pins
        /// the current retrievecolumn structure (indicated by i) and then
        /// recursively calls itself until all structures are pinned. This
        /// is done because it isn't possible to create an arbitrary number
        /// of pinned variables in a method.
        /// </summary>
        /// <param name="sesid">
        /// The session to use.
        /// </param>
        /// <param name="tableid">
        /// The table to retrieve from.
        /// </param>
        /// <param name="nativeretrievecolumns">
        /// The nativeretrievecolumns structure.</param>
        /// <param name="retrievecolumns">
        /// The managed retrieve columns structure.
        /// </param>
        /// <param name="numColumns">The number of columns.</param>
        /// <param name="i">The column currently being processed.</param>
        /// <returns>An error code from JetRetrieveColumns.</returns>
        private static unsafe int PinColumnsAndRetrieve(
            JET_SESID sesid,
            JET_TABLEID tableid,
            NATIVE_RETRIEVECOLUMN *nativeretrievecolumns,
            IList <JET_RETRIEVECOLUMN> retrievecolumns,
            int numColumns,
            int i)
        {
            // If consecutive JET_RETRIEVECOLUMN structures are using the same buffer then only pin it once.
            fixed(byte *pinnedBuffer = retrievecolumns[i].pvData)
            {
                do
                {
                    retrievecolumns[i].CheckDataSize();
                    retrievecolumns[i].GetNativeRetrievecolumn(ref nativeretrievecolumns[i]);
                    nativeretrievecolumns[i].pvData = new IntPtr(pinnedBuffer + retrievecolumns[i].ibData);
                    i++;
                }while (i < numColumns && retrievecolumns[i].pvData == retrievecolumns[i - 1].pvData);

                return(i == numColumns?
                       Impl.JetRetrieveColumns(sesid, tableid, nativeretrievecolumns, numColumns)
                    : PinColumnsAndRetrieve(sesid, tableid, nativeretrievecolumns, retrievecolumns, numColumns, i));
            }
        }
Esempio n. 3
0
 public static unsafe extern int JetRetrieveColumns(
     IntPtr sesid, IntPtr tableid, [In][Out] NATIVE_RETRIEVECOLUMN *psetcolumn, uint csetcolumn);
Esempio n. 4
0
        /// <summary>
        /// Recursive RetrieveColumns method for data pinning. This should pin a buffer and
        /// call the inherited RetrieveColumns method.
        /// </summary>
        /// <param name="sesid">The session to use.</param>
        /// <param name="tableid">
        /// The table to retrieve the columns from.
        /// </param>
        /// <param name="columnValues">
        /// Column values to retrieve.
        /// </param>
        /// <returns>An error code.</returns>
        internal static int RetrieveColumns(JET_SESID sesid, JET_TABLEID tableid, ColumnValue[] columnValues)
        {
            if (columnValues.Length > 1024)
            {
                throw new ArgumentOutOfRangeException("columnValues", columnValues.Length, "Too many column values");
            }

            int err;

            unsafe
            {
                NATIVE_RETRIEVECOLUMN *nativeRetrievecolumns = stackalloc NATIVE_RETRIEVECOLUMN[columnValues.Length];
                byte[] buffer = memoryCache.Allocate();
                fixed(byte *pinnedBuffer = buffer)
                {
                    byte *currentBuffer            = pinnedBuffer;
                    int   numVariableLengthColumns = columnValues.Length;

                    // First the fixed-size columns
                    for (int i = 0; i < columnValues.Length; ++i)
                    {
                        if (0 != columnValues[i].Size)
                        {
                            columnValues[i].MakeNativeRetrieveColumn(ref nativeRetrievecolumns[i]);
                            nativeRetrievecolumns[i].pvData = new IntPtr(currentBuffer);
                            nativeRetrievecolumns[i].cbData = checked ((uint)columnValues[i].Size);

                            currentBuffer += nativeRetrievecolumns[i].cbData;
                            Debug.Assert(currentBuffer <= pinnedBuffer + buffer.Length, "Moved past end of pinned buffer");

                            numVariableLengthColumns--;
                        }
                    }

                    // Now the variable-length columns
                    if (numVariableLengthColumns > 0)
                    {
                        int bufferUsed      = checked ((int)(currentBuffer - pinnedBuffer));
                        int bufferRemaining = checked (buffer.Length - bufferUsed);
                        int bufferPerColumn = bufferRemaining / numVariableLengthColumns;
                        Debug.Assert(bufferPerColumn > 0, "Not enough buffer left to retrieve variable length columns");

                        // Now the variable-size columns
                        for (int i = 0; i < columnValues.Length; ++i)
                        {
                            if (0 == columnValues[i].Size)
                            {
                                columnValues[i].MakeNativeRetrieveColumn(ref nativeRetrievecolumns[i]);
                                nativeRetrievecolumns[i].pvData = new IntPtr(currentBuffer);
                                nativeRetrievecolumns[i].cbData = checked ((uint)bufferPerColumn);
                                currentBuffer += nativeRetrievecolumns[i].cbData;
                                Debug.Assert(currentBuffer <= pinnedBuffer + buffer.Length, "Moved past end of pinned buffer");
                            }
                        }
                    }

                    // Retrieve the columns
                    err = Api.Impl.JetRetrieveColumns(sesid, tableid, nativeRetrievecolumns, columnValues.Length);

                    // Propagate the errors.
                    for (int i = 0; i < columnValues.Length; ++i)
                    {
                        columnValues[i].Error = (JET_err)nativeRetrievecolumns[i].err;
                    }

                    // Now parse out the columns that were retrieved successfully
                    for (int i = 0; i < columnValues.Length; ++i)
                    {
                        if (nativeRetrievecolumns[i].err >= (int)JET_err.Success &&
                            nativeRetrievecolumns[i].err != (int)JET_wrn.BufferTruncated)
                        {
                            byte *columnBuffer = (byte *)nativeRetrievecolumns[i].pvData;
                            int   startIndex   = checked ((int)(columnBuffer - pinnedBuffer));
                            columnValues[i].GetValueFromBytes(
                                buffer,
                                startIndex,
                                checked ((int)nativeRetrievecolumns[i].cbActual),
                                nativeRetrievecolumns[i].err);
                        }
                    }
                }

                memoryCache.Free(buffer);

                // Finally retrieve the buffers where the columns weren't large enough.
                RetrieveTruncatedBuffers(sesid, tableid, columnValues, nativeRetrievecolumns);
            }

            return(err);
        }
Esempio n. 5
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);
                }
            }
        }
Esempio n. 6
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);
                }
            }
        }
Esempio n. 7
0
        /// <summary>
        /// Recursive RetrieveColumns method for data pinning. This should pin a buffer and
        /// call the inherited RetrieveColumns method.
        /// </summary>
        /// <param name="sesid">The session to use.</param>
        /// <param name="tableid">
        /// The table to retrieve the columns from.
        /// </param>
        /// <param name="columnValues">
        /// Column values to retrieve.
        /// </param>
        internal static void RetrieveColumns(JET_SESID sesid, JET_TABLEID tableid, ColumnValue[] columnValues)
        {
            const int MaxColumnValues = 1024;

            if (columnValues.Length > MaxColumnValues)
            {
                throw new ArgumentOutOfRangeException("columnValues", columnValues.Length, "Too many column values");
            }

            unsafe
            {
                byte[] buffer = null;
                NATIVE_RETRIEVECOLUMN *nativeRetrievecolumns = stackalloc NATIVE_RETRIEVECOLUMN[columnValues.Length];

                try
                {
                    buffer = Caches.ColumnCache.Allocate();
                    Debug.Assert(MaxColumnValues * 16 < buffer.Length, "Maximum size of fixed columns could exceed buffer size");

                    fixed(byte *pinnedBuffer = buffer)
                    {
                        byte *currentBuffer            = pinnedBuffer;
                        int   numVariableLengthColumns = columnValues.Length;

                        // First the fixed-size columns
                        for (int i = 0; i < columnValues.Length; ++i)
                        {
                            if (0 != columnValues[i].Size)
                            {
                                columnValues[i].MakeNativeRetrieveColumn(ref nativeRetrievecolumns[i]);
                                nativeRetrievecolumns[i].pvData = new IntPtr(currentBuffer);
                                nativeRetrievecolumns[i].cbData = checked ((uint)columnValues[i].Size);

                                currentBuffer += nativeRetrievecolumns[i].cbData;
                                Debug.Assert(currentBuffer <= pinnedBuffer + buffer.Length, "Moved past end of pinned buffer");

                                numVariableLengthColumns--;
                            }
                        }

                        // Now the variable-length columns
                        if (numVariableLengthColumns > 0)
                        {
                            int bufferUsed      = checked ((int)(currentBuffer - pinnedBuffer));
                            int bufferRemaining = checked (buffer.Length - bufferUsed);
                            int bufferPerColumn = bufferRemaining / numVariableLengthColumns;
                            Debug.Assert(bufferPerColumn > 0, "Not enough buffer left to retrieve variable length columns");

                            // Now the variable-size columns
                            for (int i = 0; i < columnValues.Length; ++i)
                            {
                                if (0 == columnValues[i].Size)
                                {
                                    columnValues[i].MakeNativeRetrieveColumn(ref nativeRetrievecolumns[i]);
                                    nativeRetrievecolumns[i].pvData = new IntPtr(currentBuffer);
                                    nativeRetrievecolumns[i].cbData = checked ((uint)bufferPerColumn);
                                    currentBuffer += nativeRetrievecolumns[i].cbData;
                                    Debug.Assert(currentBuffer <= pinnedBuffer + buffer.Length, "Moved past end of pinned buffer");
                                }
                            }
                        }

                        // Retrieve the columns
                        Api.Check(Api.Impl.JetRetrieveColumns(sesid, tableid, nativeRetrievecolumns, columnValues.Length));

                        // Propagate the warnings and output.
                        for (int i = 0; i < columnValues.Length; ++i)
                        {
                            columnValues[i].Error        = (JET_wrn)nativeRetrievecolumns[i].err;
                            columnValues[i].ItagSequence = (int)nativeRetrievecolumns[i].itagSequence;
                        }

                        // Now parse out the columns that were retrieved successfully
                        for (int i = 0; i < columnValues.Length; ++i)
                        {
                            if (nativeRetrievecolumns[i].err != (int)JET_wrn.BufferTruncated)
                            {
                                byte *columnBuffer = (byte *)nativeRetrievecolumns[i].pvData;
                                int   startIndex   = checked ((int)(columnBuffer - pinnedBuffer));
                                columnValues[i].GetValueFromBytes(
                                    buffer,
                                    startIndex,
                                    checked ((int)nativeRetrievecolumns[i].cbActual),
                                    nativeRetrievecolumns[i].err);
                            }
                        }
                    }

                    // Finally retrieve the buffers where the columns weren't large enough.
                    RetrieveTruncatedBuffers(sesid, tableid, columnValues, nativeRetrievecolumns);
                }
                finally
                {
                    if (buffer != null)
                    {
                        Caches.ColumnCache.Free(ref buffer);
                    }
                }
            }
        }