/// <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); } } } }
/// <summary> /// Recursive SetColumns method for data pinning. This should populate the buffer and /// call the inherited SetColumns method. /// </summary> /// <param name="sesid">The session to use.</param> /// <param name="tableid"> /// The table to set the columns in. An update should be prepared. /// </param> /// <param name="columnValues"> /// Column values to set. /// </param> /// <param name="nativeColumns"> /// Structures to put the pinned data in. /// </param> /// <param name="i">Offset of this object in the array.</param> /// <returns>An error code.</returns> internal abstract unsafe int SetColumns(JET_SESID sesid, JET_TABLEID tableid, ColumnValue[] columnValues, NATIVE_SETCOLUMN *nativeColumns, int i);
/// <summary> /// Recursive SetColumns method for data pinning. This populates the buffer and /// calls the inherited SetColumns method. /// </summary> /// <param name="sesid">The session to use.</param> /// <param name="tableid"> /// The table to set the columns in. An update should be prepared. /// </param> /// <param name="columnValues"> /// Column values to set. /// </param> /// <param name="nativeColumns"> /// Structures to put the pinned data in. /// </param> /// <param name="i">Offset of this object in the array.</param> /// <returns>An error code.</returns> internal override unsafe int SetColumns(JET_SESID sesid, JET_TABLEID tableid, ColumnValue[] columnValues, NATIVE_SETCOLUMN *nativeColumns, int i) { var data = this.Value.GetValueOrDefault(); return(this.SetColumns(sesid, tableid, columnValues, nativeColumns, i, &data, sizeof(double), this.Value.HasValue)); }