//----------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. // //----------------------------------------------------------------------- namespace Microsoft.Isam.Esent.Interop { using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; #if MANAGEDESENT_SUPPORTS_SERIALIZATION using System.Runtime.Serialization.Formatters.Binary; #endif using System.Text; /// /// Helper methods for the ESENT API. These do data conversion for /// setting columns. /// public static partial class Api { /// /// Modifies a single column value in a modified record to be inserted or to /// update the current record. /// /// The session to use. /// The cursor to update. An update should be prepared. /// The columnid to set. /// The data to set. /// The encoding used to convert the string. public static void SetColumn( JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, string data, Encoding encoding) { SetColumn(sesid, tableid, columnid, data, encoding, SetColumnGrbit.None); } /// /// Modifies a single column value in a modified record to be inserted or to /// update the current record. /// /// The session to use. /// The cursor to update. An update should be prepared. /// The columnid to set. /// The data to set. /// The encoding used to convert the string. /// SetColumn options. public static void SetColumn( JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, string data, Encoding encoding, SetColumnGrbit grbit) { CheckEncodingIsValid(encoding); if (null == data) { JetSetColumn(sesid, tableid, columnid, null, 0, grbit, null); } else if (0 == data.Length) { JetSetColumn(sesid, tableid, columnid, null, 0, grbit | SetColumnGrbit.ZeroLength, null); } else if (Encoding.Unicode == encoding) { // Optimization for setting Unicode strings. unsafe { fixed (char* buffer = data) { JetSetColumn( sesid, tableid, columnid, new IntPtr(buffer), checked(data.Length * sizeof(char)), grbit, null); } } } else if (encoding.GetMaxByteCount(data.Length) <= Caches.ColumnCache.BufferSize) { #if MANAGEDESENT_ON_WSA // Encoding.GetBytes(char*, int, byte*, int) overload is missing in new Windows UI. // So we can't use the ColumnCache. We'll just use a different GetBytes() overload. byte[] buffer = encoding.GetBytes(data); unsafe { fixed (byte* bytes = buffer) { JetSetColumn(sesid, tableid, columnid, new IntPtr(bytes), buffer.Length, grbit, null); } } #else // The encoding output will fix in a cached buffer. Get one to avoid // more memory allocations. byte[] buffer = null; try { buffer = Caches.ColumnCache.Allocate(); unsafe { fixed (char* chars = data) fixed (byte* bytes = buffer) { int dataSize = encoding.GetBytes(chars, data.Length, bytes, buffer.Length); JetSetColumn(sesid, tableid, columnid, new IntPtr(bytes), dataSize, grbit, null); } } } finally { if (buffer != null) { Caches.ColumnCache.Free(ref buffer); } } #endif } else { byte[] bytes = encoding.GetBytes(data); JetSetColumn(sesid, tableid, columnid, bytes, bytes.Length, grbit, null); } } /// /// Modifies a single column value in a modified record to be inserted or to /// update the current record. /// /// The session to use. /// The cursor to update. An update should be prepared. /// The columnid to set. /// The data to set. public static void SetColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, byte[] data) { SetColumn(sesid, tableid, columnid, data, SetColumnGrbit.None); } /// /// Modifies a single column value in a modified record to be inserted or to /// update the current record. /// /// The session to use. /// The cursor to update. An update should be prepared. /// The columnid to set. /// The data to set. /// SetColumn options. public static void SetColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, byte[] data, SetColumnGrbit grbit) { if ((null != data) && (0 == data.Length)) { grbit |= SetColumnGrbit.ZeroLength; } int dataLength = (null == data) ? 0 : data.Length; JetSetColumn(sesid, tableid, columnid, data, dataLength, grbit, null); } /// /// Modifies a single column value in a modified record to be inserted or to /// update the current record. /// /// The session to use. /// The cursor to update. An update should be prepared. /// The columnid to set. /// The data to set. public static void SetColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, bool data) { byte b = data ? (byte)0xff : (byte)0x0; SetColumn(sesid, tableid, columnid, b); } /// /// Modifies a single column value in a modified record to be inserted or to /// update the current record. /// /// The session to use. /// The cursor to update. An update should be prepared. /// The columnid to set. /// The data to set. public static void SetColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, byte data) { unsafe { const int DataSize = sizeof(byte); var pointer = new IntPtr(&data); JetSetColumn(sesid, tableid, columnid, pointer, DataSize, SetColumnGrbit.None, null); } } /// /// Modifies a single column value in a modified record to be inserted or to /// update the current record. /// /// The session to use. /// The cursor to update. An update should be prepared. /// The columnid to set. /// The data to set. public static void SetColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, short data) { unsafe { const int DataSize = sizeof(short); var pointer = new IntPtr(&data); JetSetColumn(sesid, tableid, columnid, pointer, DataSize, SetColumnGrbit.None, null); } } /// /// Modifies a single column value in a modified record to be inserted or to /// update the current record. /// /// The session to use. /// The cursor to update. An update should be prepared. /// The columnid to set. /// The data to set. public static void SetColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, int data) { unsafe { const int DataSize = sizeof(int); var pointer = new IntPtr(&data); JetSetColumn(sesid, tableid, columnid, pointer, DataSize, SetColumnGrbit.None, null); } } /// /// Modifies a single column value in a modified record to be inserted or to /// update the current record. /// /// The session to use. /// The cursor to update. An update should be prepared. /// The columnid to set. /// The data to set. public static void SetColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, long data) { unsafe { const int DataSize = sizeof(long); var pointer = new IntPtr(&data); JetSetColumn(sesid, tableid, columnid, pointer, DataSize, SetColumnGrbit.None, null); } } /// /// Modifies a single column value in a modified record to be inserted or to /// update the current record. /// /// The session to use. /// The cursor to update. An update should be prepared. /// The columnid to set. /// The data to set. public static void SetColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, Guid data) { unsafe { const int DataSize = 16; // sizeof(Guid) isn't a compile-time constant var pointer = new IntPtr(&data); JetSetColumn(sesid, tableid, columnid, pointer, DataSize, SetColumnGrbit.None, null); } } /// /// Modifies a single column value in a modified record to be inserted or to /// update the current record. /// /// The session to use. /// The cursor to update. An update should be prepared. /// The columnid to set. /// The data to set. public static void SetColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, DateTime data) { SetColumn(sesid, tableid, columnid, data.ToOADate()); } /// /// Modifies a single column value in a modified record to be inserted or to /// update the current record. /// /// The session to use. /// The cursor to update. An update should be prepared. /// The columnid to set. /// The data to set. public static void SetColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, float data) { unsafe { const int DataSize = sizeof(float); var pointer = new IntPtr(&data); JetSetColumn(sesid, tableid, columnid, pointer, DataSize, SetColumnGrbit.None, null); } } /// /// Modifies a single column value in a modified record to be inserted or to /// update the current record. /// /// The session to use. /// The cursor to update. An update should be prepared. /// The columnid to set. /// The data to set. public static void SetColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, double data) { unsafe { const int DataSize = sizeof(double); var pointer = new IntPtr(&data); JetSetColumn(sesid, tableid, columnid, pointer, DataSize, SetColumnGrbit.None, null); } } /// /// Perform atomic addition on one column. The column must be of type /// . This function allows multiple sessions to update the /// same record concurrently without conflicts. /// /// /// This method wraps . /// /// The session to use. /// The cursor to update. /// The column to update. This must be an escrow-updatable column. /// The delta to apply to the column. /// The current value of the column as stored in the database (versioning is ignored). public static int EscrowUpdate(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, int delta) { var previousValue = new byte[sizeof(int)]; int actualPreviousValueLength; JetEscrowUpdate( sesid, tableid, columnid, BitConverter.GetBytes(delta), sizeof(int), previousValue, previousValue.Length, out actualPreviousValueLength, EscrowUpdateGrbit.None); Debug.Assert( previousValue.Length == actualPreviousValueLength, "Unexpected previous value length. Expected an Int32"); return BitConverter.ToInt32(previousValue, 0); } /// /// Modifies a single column value in a modified record to be inserted or to /// update the current record. /// /// The session to use. /// The cursor to update. An update should be prepared. /// The columnid to set. /// The data to set. [CLSCompliant(false)] public static void SetColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, ushort data) { unsafe { const int DataSize = sizeof(ushort); var pointer = new IntPtr(&data); JetSetColumn(sesid, tableid, columnid, pointer, DataSize, SetColumnGrbit.None, null); } } /// /// Modifies a single column value in a modified record to be inserted or to /// update the current record. /// /// The session to use. /// The cursor to update. An update should be prepared. /// The columnid to set. /// The data to set. [CLSCompliant(false)] public static void SetColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, uint data) { unsafe { const int DataSize = sizeof(uint); var pointer = new IntPtr(&data); JetSetColumn(sesid, tableid, columnid, pointer, DataSize, SetColumnGrbit.None, null); } } /// /// Modifies a single column value in a modified record to be inserted or to /// update the current record. /// /// The session to use. /// The cursor to update. An update should be prepared. /// The columnid to set. /// The data to set. [CLSCompliant(false)] public static void SetColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, ulong data) { unsafe { const int DataSize = sizeof(ulong); var pointer = new IntPtr(&data); JetSetColumn(sesid, tableid, columnid, pointer, DataSize, SetColumnGrbit.None, null); } } #if MANAGEDESENT_SUPPORTS_SERIALIZATION /// /// Write a serialized form of an object to a column. /// /// The session to use. /// The table to write to. An update should be prepared. /// The column to write to. /// The object to write. The object must be serializable. [SuppressMessage("Exchange.Security", "EX0043:DoNotUseBinarySoapFormatter", Justification = "Suppress warning in current code base.The usage has already been verified.")] public static void SerializeObjectToColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, object value) { if (null == value) { Api.SetColumn(sesid, tableid, columnid, null); } else { using (var stream = new ColumnStream(sesid, tableid, columnid)) { var serializer = new BinaryFormatter { Context = new StreamingContext(StreamingContextStates.Persistence) }; serializer.Serialize(stream, value); } } } #endif /// /// Sets columns from ColumnValue objects. /// /// The session to use. /// The cursor to update. An update should be prepared. /// The values to set. public static void SetColumns(JET_SESID sesid, JET_TABLEID tableid, params ColumnValue[] values) { if (null == values) { throw new ArgumentNullException("values"); } if (0 == values.Length) { throw new ArgumentOutOfRangeException("values", values.Length, "must have at least one value"); } unsafe { NATIVE_SETCOLUMN* nativeSetcolumns = stackalloc NATIVE_SETCOLUMN[values.Length]; Api.Check(values[0].SetColumns(sesid, tableid, values, nativeSetcolumns, 0)); } } /// /// Verifies that the given encoding is valid for setting/retrieving data. Only /// the ASCII and Unicode encodings are allowed. An /// is thrown if the encoding isn't valid. /// /// The encoding to check. private static void CheckEncodingIsValid(Encoding encoding) { #if MANAGEDESENT_ON_CORECLR string webName = encoding.WebName; if (webName != "utf-8" && webName != "utf-16") { throw new ArgumentOutOfRangeException( "encoding", webName, "Invalid Encoding type. Only Unicode (utf-8 and utf-16) encodings are allowed."); } #else const int AsciiCodePage = 20127; // from MSDN const int UnicodeCodePage = 1200; // from MSDN int codePage = encoding.CodePage; if ((AsciiCodePage != codePage) && (UnicodeCodePage != codePage)) { throw new ArgumentOutOfRangeException( "encoding", codePage, "Invalid Encoding type. Only ASCII and Unicode encodings are allowed"); } #endif } } }