//-----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation.
//
//-----------------------------------------------------------------------
namespace Microsoft.Isam.Esent.Interop
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
#if MANAGEDESENT_SUPPORTS_SERIALIZATION
using System.Runtime.Serialization.Formatters.Binary;
#endif
using System.Text;
///
/// Helper methods for the ESENT API. These aren't interop versions
/// of the API, but encapsulate very common uses of the functions.
///
public static partial class Api
{
///
/// Encoding to use to decode ASCII text. We use this because
/// UTF8.GetString is faster than ASCII.GetString.
///
private static readonly Encoding AsciiDecoder = new UTF8Encoding(false, true);
///
/// Retrieves the bookmark for the record that is associated with the index entry
/// at the current position of a cursor. This bookmark can then be used to
/// reposition that cursor back to the same record using JetGotoBookmark.
///
/// The session to use.
/// The cursor to retrieve the bookmark from.
/// The bookmark of the record.
public static byte[] GetBookmark(JET_SESID sesid, JET_TABLEID tableid)
{
byte[] buffer = null;
byte[] bookmark;
try
{
buffer = Caches.BookmarkCache.Allocate();
int bookmarkSize;
Api.JetGetBookmark(sesid, tableid, buffer, buffer.Length, out bookmarkSize);
bookmark = MemoryCache.Duplicate(buffer, bookmarkSize);
}
finally
{
if (buffer != null)
{
Caches.BookmarkCache.Free(ref buffer);
}
}
return bookmark;
}
///
/// Retrieves the bookmark for the record that is associated with the index entry
/// at the current position of a cursor. This bookmark can then be used to
/// reposition that cursor back to the same record using JetGotoBookmark.
///
/// The session to use.
/// The cursor to retrieve the bookmark from.
/// Returns the primary bookmark.
/// The secondary bookmark of the record.
public static byte[] GetSecondaryBookmark(
JET_SESID sesid,
JET_TABLEID tableid,
out byte[] primaryBookmark)
{
byte[] bufferPrimary = null;
byte[] bufferSecondary = null;
byte[] secondaryBookmark;
primaryBookmark = null;
try
{
bufferPrimary = Caches.BookmarkCache.Allocate();
bufferSecondary = Caches.SecondaryBookmarkCache.Allocate();
int bookmarkSizePrimary;
int bookmarkSizeSecondary;
Api.JetGetSecondaryIndexBookmark(
sesid,
tableid,
bufferSecondary,
bufferSecondary.Length,
out bookmarkSizeSecondary,
bufferPrimary,
bufferPrimary.Length,
out bookmarkSizePrimary,
GetSecondaryIndexBookmarkGrbit.None);
primaryBookmark = MemoryCache.Duplicate(bufferPrimary, bookmarkSizePrimary);
secondaryBookmark = MemoryCache.Duplicate(bufferSecondary, bookmarkSizeSecondary);
}
finally
{
if (bufferPrimary != null)
{
Caches.BookmarkCache.Free(ref bufferPrimary);
}
if (bufferSecondary != null)
{
Caches.BookmarkCache.Free(ref bufferSecondary);
}
}
return secondaryBookmark;
}
///
/// Retrieves the key for the index entry at the current position of a cursor.
///
/// The session to use.
/// The cursor to retrieve the key from.
/// Retrieve key options.
/// The retrieved key.
public static byte[] RetrieveKey(JET_SESID sesid, JET_TABLEID tableid, RetrieveKeyGrbit grbit)
{
byte[] buffer = null;
byte[] key;
try
{
buffer = Caches.BookmarkCache.Allocate();
int keySize;
Api.JetRetrieveKey(sesid, tableid, buffer, buffer.Length, out keySize, grbit);
key = MemoryCache.Duplicate(buffer, keySize);
}
finally
{
if (buffer != null)
{
Caches.BookmarkCache.Free(ref buffer);
}
}
return key;
}
///
/// 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.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// The size of the column. 0 if the column is null.
public static int? RetrieveColumnSize(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid)
{
return RetrieveColumnSize(sesid, tableid, columnid, 1, RetrieveColumnGrbit.None);
}
///
/// 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.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
///
/// 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.
///
/// Retrieve column options.
/// The size of the column. 0 if the column is null.
public static int? RetrieveColumnSize(
JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, int itagSequence, RetrieveColumnGrbit grbit)
{
// We cannot support this request when there is no way to indicate that a column reference is returned.
if ((grbit & (RetrieveColumnGrbit)0x00020000) != 0) // UnpublishedGrbits.RetrieveAsRefIfNotInRecord
{
throw new EsentInvalidGrbitException();
}
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;
}
///
/// 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.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// Retrieve column options.
///
/// 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).
///
/// The data retrieved from the column. Null if the column is null.
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 = null;
byte[] data;
try
{
cache = Caches.ColumnCache.Allocate();
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.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);
}
}
}
finally
{
if (cache != null)
{
Caches.ColumnCache.Free(ref cache);
}
}
return data;
}
///
/// 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.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// The data retrieved from the column. Null if the column is null.
public static byte[] RetrieveColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid)
{
return RetrieveColumn(sesid, tableid, columnid, RetrieveColumnGrbit.None, null);
}
///
/// 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.
/// The Unicode encoding is used.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// The data retrieved from the column as a string. Null if the column is null.
public static string RetrieveColumnAsString(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid)
{
return RetrieveColumnAsString(sesid, tableid, columnid, Encoding.Unicode, RetrieveColumnGrbit.None);
}
///
/// 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.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// The string encoding to use when converting data.
/// The data retrieved from the column as a string. Null if the column is null.
public static string RetrieveColumnAsString(
JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, Encoding encoding)
{
return RetrieveColumnAsString(sesid, tableid, columnid, encoding, RetrieveColumnGrbit.None);
}
///
/// 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.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// The string encoding to use when converting data.
/// Retrieval options.
/// The data retrieved from the column as a string. Null if the column is null.
public static string RetrieveColumnAsString(
JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, Encoding encoding, RetrieveColumnGrbit grbit)
{
// This is an optimization for retrieving Unicode strings
if (Encoding.Unicode == encoding)
{
return RetrieveUnicodeString(sesid, tableid, columnid, grbit);
}
// 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 = null;
string s;
try
{
cachedBuffer = Caches.ColumnCache.Allocate();
byte[] data = cachedBuffer;
int dataSize;
JET_wrn 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);
}
}
#if MANAGEDESENT_SUPPORTS_ANSI
// If we are about to decode ASCII data then use the UTF8 decoder instead. This
// is done because the UTF8 decoder is faster and will produce the same results
// on ASCII data. Different results will be produced on invalid data, but that
// behaviour can be considered undefined.
Encoding decoder = (encoding is ASCIIEncoding) ? AsciiDecoder : encoding;
#else
Encoding decoder = encoding;
#endif
s = decoder.GetString(data, 0, dataSize);
}
finally
{
if (cachedBuffer != null)
{
// Now we have extracted the string from the buffer we can free (cache) the buffer.
Caches.ColumnCache.Free(ref cachedBuffer);
}
}
return s;
}
///
/// 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.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// The data retrieved from the column as a short. Null if the column is null.
public static short? RetrieveColumnAsInt16(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid)
{
return RetrieveColumnAsInt16(sesid, tableid, columnid, RetrieveColumnGrbit.None);
}
///
/// Retrieves an int16 column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// Retrieval options.
/// The data retrieved from the column as a short. Null if the column is null.
public static short? RetrieveColumnAsInt16(
JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit)
{
// We cannot support this request when there is no way to indicate that a column reference is returned.
if ((grbit & (RetrieveColumnGrbit)0x00020000) != 0) // UnpublishedGrbits.RetrieveAsRefIfNotInRecord
{
throw new EsentInvalidGrbitException();
}
unsafe
{
const int DataSize = sizeof(short);
short data;
var pointer = new IntPtr(&data);
int actualDataSize;
JET_wrn wrn = JetRetrieveColumn(
sesid, tableid, columnid, pointer, DataSize, out actualDataSize, grbit, null);
return CreateReturnValue(data, DataSize, wrn, actualDataSize);
}
}
///
/// 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.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// The data retrieved from the column as an int. Null if the column is null.
public static int? RetrieveColumnAsInt32(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid)
{
return RetrieveColumnAsInt32(sesid, tableid, columnid, RetrieveColumnGrbit.None);
}
///
/// Retrieves an int32 column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// Retrieval options.
/// The data retrieved from the column as an int. Null if the column is null.
public static int? RetrieveColumnAsInt32(
JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit)
{
// We cannot support this request when there is no way to indicate that a column reference is returned.
if ((grbit & (RetrieveColumnGrbit)0x00020000) != 0) // UnpublishedGrbits.RetrieveAsRefIfNotInRecord
{
throw new EsentInvalidGrbitException();
}
unsafe
{
const int DataSize = sizeof(int);
int data;
var pointer = new IntPtr(&data);
int actualDataSize;
JET_wrn wrn = JetRetrieveColumn(
sesid, tableid, columnid, pointer, DataSize, out actualDataSize, grbit, null);
return CreateReturnValue(data, DataSize, wrn, actualDataSize);
}
}
///
/// 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.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// The data retrieved from the column as a long. Null if the column is null.
public static long? RetrieveColumnAsInt64(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid)
{
return RetrieveColumnAsInt64(sesid, tableid, columnid, RetrieveColumnGrbit.None);
}
///
/// 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.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// Retrieval options.
/// The data retrieved from the column as a long. Null if the column is null.
public static long? RetrieveColumnAsInt64(
JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit)
{
// We cannot support this request when there is no way to indicate that a column reference is returned.
if ((grbit & (RetrieveColumnGrbit)0x00020000) != 0) // UnpublishedGrbits.RetrieveAsRefIfNotInRecord
{
throw new EsentInvalidGrbitException();
}
unsafe
{
const int DataSize = sizeof(long);
long data;
var pointer = new IntPtr(&data);
int actualDataSize;
JET_wrn wrn = JetRetrieveColumn(
sesid, tableid, columnid, pointer, DataSize, out actualDataSize, grbit, null);
return CreateReturnValue(data, DataSize, wrn, actualDataSize);
}
}
///
/// Retrieves a float column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// The data retrieved from the column as a float. Null if the column is null.
public static float? RetrieveColumnAsFloat(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid)
{
return RetrieveColumnAsFloat(sesid, tableid, columnid, RetrieveColumnGrbit.None);
}
///
/// Retrieves a float column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// Retrieval options.
/// The data retrieved from the column as a float. Null if the column is null.
public static float? RetrieveColumnAsFloat(
JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit)
{
// We cannot support this request when there is no way to indicate that a column reference is returned.
if ((grbit & (RetrieveColumnGrbit)0x00020000) != 0) // UnpublishedGrbits.RetrieveAsRefIfNotInRecord
{
throw new EsentInvalidGrbitException();
}
unsafe
{
const int DataSize = sizeof(float);
float data;
var pointer = new IntPtr(&data);
int actualDataSize;
JET_wrn wrn = JetRetrieveColumn(
sesid, tableid, columnid, pointer, DataSize, out actualDataSize, grbit, null);
return CreateReturnValue(data, DataSize, wrn, actualDataSize);
}
}
///
/// Retrieves a double column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// The data retrieved from the column as a double. Null if the column is null.
public static double? RetrieveColumnAsDouble(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid)
{
return RetrieveColumnAsDouble(sesid, tableid, columnid, RetrieveColumnGrbit.None);
}
///
/// Retrieves a double column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// Retrieval options.
/// The data retrieved from the column as a double. Null if the column is null.
public static double? RetrieveColumnAsDouble(
JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit)
{
// We cannot support this request when there is no way to indicate that a column reference is returned.
if ((grbit & (RetrieveColumnGrbit)0x00020000) != 0) // UnpublishedGrbits.RetrieveAsRefIfNotInRecord
{
throw new EsentInvalidGrbitException();
}
unsafe
{
const int DataSize = sizeof(double);
double data;
var pointer = new IntPtr(&data);
int actualDataSize;
JET_wrn wrn = JetRetrieveColumn(
sesid, tableid, columnid, pointer, DataSize, out actualDataSize, grbit, null);
return CreateReturnValue(data, DataSize, wrn, actualDataSize);
}
}
///
/// Retrieves a boolean column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// The data retrieved from the column as a boolean. Null if the column is null.
public static bool? RetrieveColumnAsBoolean(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid)
{
return RetrieveColumnAsBoolean(sesid, tableid, columnid, RetrieveColumnGrbit.None);
}
///
/// Retrieves a boolean column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// Retrieval options.
/// The data retrieved from the column as a boolean. Null if the column is null.
public static bool? RetrieveColumnAsBoolean(
JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit)
{
byte? b = RetrieveColumnAsByte(sesid, tableid, columnid, grbit);
if (b.HasValue)
{
return 0 != b.Value;
}
return new bool?();
}
///
/// Retrieves a byte column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// The data retrieved from the column as a byte. Null if the column is null.
public static byte? RetrieveColumnAsByte(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid)
{
return RetrieveColumnAsByte(sesid, tableid, columnid, RetrieveColumnGrbit.None);
}
///
/// Retrieves a byte column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// Retrieval options.
/// The data retrieved from the column as a byte. Null if the column is null.
public static byte? RetrieveColumnAsByte(
JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit)
{
// We cannot support this request when there is no way to indicate that a column reference is returned.
if ((grbit & (RetrieveColumnGrbit)0x00020000) != 0) // UnpublishedGrbits.RetrieveAsRefIfNotInRecord
{
throw new EsentInvalidGrbitException();
}
unsafe
{
const int DataSize = sizeof(byte);
byte data;
var pointer = new IntPtr(&data);
int actualDataSize;
JET_wrn wrn = JetRetrieveColumn(
sesid, tableid, columnid, pointer, DataSize, out actualDataSize, grbit, null);
return CreateReturnValue(data, DataSize, wrn, actualDataSize);
}
}
///
/// Retrieves a guid column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// The data retrieved from the column as a guid. Null if the column is null.
public static Guid? RetrieveColumnAsGuid(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid)
{
return RetrieveColumnAsGuid(sesid, tableid, columnid, RetrieveColumnGrbit.None);
}
///
/// Retrieves a guid column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// Retrieval options.
/// The data retrieved from the column as a guid. Null if the column is null.
public static Guid? RetrieveColumnAsGuid(
JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit)
{
// We cannot support this request when there is no way to indicate that a column reference is returned.
if ((grbit & (RetrieveColumnGrbit)0x00020000) != 0) // UnpublishedGrbits.RetrieveAsRefIfNotInRecord
{
throw new EsentInvalidGrbitException();
}
unsafe
{
const int DataSize = 16;
Guid data;
var pointer = new IntPtr(&data);
int actualDataSize;
JET_wrn wrn = JetRetrieveColumn(
sesid, tableid, columnid, pointer, DataSize, out actualDataSize, grbit, null);
return CreateReturnValue(data, DataSize, wrn, actualDataSize);
}
}
///
/// Retrieves a datetime column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// The data retrieved from the column as a datetime. Null if the column is null.
public static DateTime? RetrieveColumnAsDateTime(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid)
{
return RetrieveColumnAsDateTime(sesid, tableid, columnid, RetrieveColumnGrbit.None);
}
///
/// Retrieves a datetime column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// Retrieval options.
/// The data retrieved from the column as a datetime. Null if the column is null.
public static DateTime? RetrieveColumnAsDateTime(
JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit)
{
// Internally DateTime is stored in OLE Automation format
double? oadate = RetrieveColumnAsDouble(sesid, tableid, columnid, grbit);
if (oadate.HasValue)
{
return Conversions.ConvertDoubleToDateTime(oadate.Value);
}
return new DateTime?();
}
///
/// Retrieves a uint16 column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// The data retrieved from the column as an UInt16. Null if the column is null.
[CLSCompliant(false)]
public static ushort? RetrieveColumnAsUInt16(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid)
{
return RetrieveColumnAsUInt16(sesid, tableid, columnid, RetrieveColumnGrbit.None);
}
///
/// Retrieves a uint16 column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// Retrieval options.
/// The data retrieved from the column as an UInt16. Null if the column is null.
[CLSCompliant(false)]
public static ushort? RetrieveColumnAsUInt16(
JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit)
{
// We cannot support this request when there is no way to indicate that a column reference is returned.
if ((grbit & (RetrieveColumnGrbit)0x00020000) != 0) // UnpublishedGrbits.RetrieveAsRefIfNotInRecord
{
throw new EsentInvalidGrbitException();
}
unsafe
{
const int DataSize = sizeof(ushort);
ushort data;
var pointer = new IntPtr(&data);
int actualDataSize;
JET_wrn wrn = JetRetrieveColumn(
sesid, tableid, columnid, pointer, DataSize, out actualDataSize, grbit, null);
return CreateReturnValue(data, DataSize, wrn, actualDataSize);
}
}
///
/// Retrieves a uint32 column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// The data retrieved from the column as an UInt32. Null if the column is null.
[CLSCompliant(false)]
public static uint? RetrieveColumnAsUInt32(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid)
{
return RetrieveColumnAsUInt32(sesid, tableid, columnid, RetrieveColumnGrbit.None);
}
///
/// Retrieves a uint32 column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// Retrieval options.
/// The data retrieved from the column as an UInt32. Null if the column is null.
[CLSCompliant(false)]
public static uint? RetrieveColumnAsUInt32(
JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit)
{
// We cannot support this request when there is no way to indicate that a column reference is returned.
if ((grbit & (RetrieveColumnGrbit)0x00020000) != 0) // UnpublishedGrbits.RetrieveAsRefIfNotInRecord
{
throw new EsentInvalidGrbitException();
}
unsafe
{
const int DataSize = sizeof(uint);
uint data;
var pointer = new IntPtr(&data);
int actualDataSize;
JET_wrn wrn = JetRetrieveColumn(
sesid, tableid, columnid, pointer, DataSize, out actualDataSize, grbit, null);
return CreateReturnValue(data, DataSize, wrn, actualDataSize);
}
}
///
/// Retrieves a uint64 column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// The data retrieved from the column as an UInt64. Null if the column is null.
[CLSCompliant(false)]
public static ulong? RetrieveColumnAsUInt64(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid)
{
return RetrieveColumnAsUInt64(sesid, tableid, columnid, RetrieveColumnGrbit.None);
}
///
/// Retrieves a uint64 column value from the current record. The record is that
/// record associated with the index entry at the current position of the cursor.
///
/// The session to use.
/// The cursor to retrieve the column from.
/// The columnid to retrieve.
/// Retrieval options.
/// The data retrieved from the column as an UInt64. Null if the column is null.
[CLSCompliant(false)]
public static ulong? RetrieveColumnAsUInt64(
JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit)
{
// We cannot support this request when there is no way to indicate that a column reference is returned.
if ((grbit & (RetrieveColumnGrbit)0x00020000) != 0) // UnpublishedGrbits.RetrieveAsRefIfNotInRecord
{
throw new EsentInvalidGrbitException();
}
unsafe
{
const int DataSize = sizeof(ulong);
ulong data;
var pointer = new IntPtr(&data);
int actualDataSize;
JET_wrn wrn = JetRetrieveColumn(
sesid, tableid, columnid, pointer, DataSize, out actualDataSize, grbit, null);
return CreateReturnValue(data, DataSize, wrn, actualDataSize);
}
}
#if MANAGEDESENT_SUPPORTS_SERIALIZATION
///
/// Deserialize an object from a column.
///
/// The session to use.
/// The table to read from.
/// The column to read from.
/// The deserialized object. Null if the column was null.
public static object DeserializeObjectFromColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid)
{
return DeserializeObjectFromColumn(sesid, tableid, columnid, RetrieveColumnGrbit.None);
}
///
/// Deserialize an object from a column.
///
/// The session to use.
/// The table to read from.
/// The column to read from.
/// The retrieval options to use.
/// The deserialized object. Null if the column was null.
[SuppressMessage("Exchange.Security", "EX0043:DoNotUseBinarySoapFormatter", Justification = "Suppress warning in current code base.The usage has already been verified.")]
public static object DeserializeObjectFromColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit)
{
// We cannot support this request when there is no way to indicate that a column reference is returned.
if ((grbit & (RetrieveColumnGrbit)0x00020000) != 0) // UnpublishedGrbits.RetrieveAsRefIfNotInRecord
{
throw new EsentInvalidGrbitException();
}
int actualSize;
if (JET_wrn.ColumnNull == Api.JetRetrieveColumn(sesid, tableid, columnid, null, 0, out actualSize, grbit, null))
{
return null;
}
using (var stream = new ColumnStream(sesid, tableid, columnid))
{
var deseriaizer = new BinaryFormatter();
return deseriaizer.Deserialize(stream);
}
}
#endif
///
/// Retrieves columns into ColumnValue objects.
///
/// The session to use.
/// The cursor retrieve the data from. The cursor should be positioned on a record.
/// The values to retrieve.
public static void RetrieveColumns(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");
}
ColumnValue.RetrieveColumns(sesid, tableid, values);
}
///
/// Efficiently retrieves a set of columns and their values from the
/// current record of a cursor or the copy buffer of that cursor.
///
/// The session to use.
/// The cursor to retrieve data from.
/// Enumerate options.
/// The discovered columns and their values.
public static IEnumerable EnumerateColumns(
JET_SESID sesid,
JET_TABLEID tableid,
EnumerateColumnsGrbit grbit)
{
IEnumerable enumeratedColumns;
Api.JetEnumerateColumns(sesid, tableid, grbit, out enumeratedColumns);
return enumeratedColumns;
}
///
/// Create the nullable return value.
///
/// The (struct) type to return.
/// The data retrieved from the column.
/// The size of the data.
/// The warning code from esent.
/// The actual size of the data retireved fomr esent.
/// A nullable struct of type T.
private static T? CreateReturnValue(T data, int dataSize, JET_wrn wrn, int actualDataSize) where T : struct
{
if (JET_wrn.ColumnNull == wrn)
{
return new T?();
}
CheckDataSize(dataSize, actualDataSize);
return data;
}
///
/// Make sure the retrieved data size is at least as long as the expected size.
/// An exception is thrown if the data isn't long enough.
///
/// The expected data size.
/// The size of the retrieved data.
private static void CheckDataSize(int expectedDataSize, int actualDataSize)
{
if (actualDataSize < expectedDataSize)
{
throw new EsentInvalidColumnException();
}
}
///
/// 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.
///
///
/// The session to use.
///
///
/// The table to retrieve from.
///
///
/// The nativeretrievecolumns structure.
///
/// The managed retrieve columns structure.
///
/// The number of columns.
/// The column currently being processed.
/// An error code from JetRetrieveColumns.
private static unsafe int PinColumnsAndRetrieve(
JET_SESID sesid,
JET_TABLEID tableid,
NATIVE_RETRIEVECOLUMN* nativeretrievecolumns,
IList 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);
}
}
///
/// Retrieve a Unicode (UTF16) string. This is optimized to take advantage of the fact
/// that no conversion is needed.
///
/// The session to use.
/// The table to retrieve from.
/// The column to retrieve.
/// Retrieve options.
/// The string retrieved from the column.
private static unsafe string RetrieveUnicodeString(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit)
{
Debug.Assert((grbit & (RetrieveColumnGrbit)0x00020000) == 0, "UnpublishedGrbits.RetrieveAsRefIfNotInRecord is not supported.");
// 512 Unicode characters (1kb on stack)
const int BufferSize = 512;
char* buffer = stackalloc char[BufferSize];
int actualDataSize;
JET_wrn wrn = JetRetrieveColumn(sesid, tableid, columnid, new IntPtr(buffer), BufferSize * sizeof(char), out actualDataSize, grbit, null);
if (JET_wrn.ColumnNull == wrn)
{
return null;
}
if (JET_wrn.Success == wrn)
{
////return StringCache.GetString(buffer, 0, actualDataSize);
return new string(buffer, 0, actualDataSize / sizeof(char));
}
Debug.Assert(JET_wrn.BufferTruncated == wrn, "Unexpected warning code");
// Create a fake string of the appropriate size and then fill it in.
var s = new string('\0', actualDataSize / sizeof(char));
fixed (char* p = s)
{
int newDataSize;
wrn = JetRetrieveColumn(sesid, tableid, columnid, new IntPtr(p), actualDataSize, out newDataSize, 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.",
actualDataSize,
newDataSize);
Trace.TraceError(error);
throw new InvalidOperationException(error);
}
}
return s;
}
}
}