/// <summary> /// Update the tableid and position the tableid on the record that was modified. /// This can be useful when inserting a record because by default the tableid /// remains in its old location. /// </summary> /// <remarks> /// Save is the final step in performing an insert or an update. The update is begun by /// calling creating an Update object and then by calling JetSetColumn or JetSetColumns one or more times /// to set the record state. Finally, Update is called to complete the update operation. /// Indexes are updated only by Update or and not during JetSetColumn or JetSetColumns /// </remarks> public void SaveAndGotoBookmark() { var bookmark = bookmarkCache.Allocate(); int actualBookmarkSize; this.Save(bookmark, bookmark.Length, out actualBookmarkSize); Api.JetGotoBookmark(this.sesid, this.tableid, bookmark, actualBookmarkSize); bookmarkCache.Free(bookmark); }
/// <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); }
/// <summary> /// Retrieves a string column value from the current record. The record is that /// record associated with the index entry at the current position of the cursor. /// </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="encoding">The string encoding to use when converting data.</param> /// <param name="grbit">Retrieval options.</param> /// <returns>The data retrieved from the column as a string. Null if the column is null.</returns> public static string RetrieveColumnAsString( JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, Encoding encoding, RetrieveColumnGrbit grbit) { JET_wrn wrn; // This is an optimization for retrieving small Unicode strings // We use a stack-allocated buffer to retrieve the data and then create // a string from the data if (Encoding.Unicode == encoding) { unsafe { // 512 bytes give 256 Unicode characters. const int BufferSize = 512; char * buffer = stackalloc char[BufferSize]; int actualDataSize; wrn = JetRetrieveColumn( sesid, tableid, columnid, new IntPtr(buffer), BufferSize, out actualDataSize, grbit, null); if (JET_wrn.ColumnNull == wrn) { return(null); } if (JET_wrn.Success == wrn) { return(new string(buffer, 0, actualDataSize / sizeof(char))); } Debug.Assert(JET_wrn.BufferTruncated == wrn, "Unexpected warning code"); // Fallthrough to normal case below } } // Retrieving a string happens in two stages: first the data is retrieved into a data // buffer and then the buffer is converted to a string. The buffer isn't needed for // very long so we try to use a cached buffer. byte[] cachedBuffer = memoryCache.Allocate(); byte[] data = cachedBuffer; int dataSize; wrn = JetRetrieveColumn(sesid, tableid, columnid, data, data.Length, out dataSize, grbit, null); if (JET_wrn.ColumnNull == wrn) { return(null); } if (JET_wrn.BufferTruncated == wrn) { Debug.Assert(dataSize > data.Length, "Expected column to be bigger than buffer"); data = new byte[dataSize]; wrn = JetRetrieveColumn(sesid, tableid, columnid, data, data.Length, out dataSize, grbit, null); if (JET_wrn.BufferTruncated == 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); } } // TODO: for Unicode strings pin the buffer and use the String(char*) constructor. string s = encoding.GetString(data, 0, dataSize); // Now we have extracted the string from the buffer we can free (cache) the buffer. memoryCache.Free(cachedBuffer); return(s); }