//-----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation.
//
//-----------------------------------------------------------------------
namespace Microsoft.Isam.Esent.Interop.Implementation
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.InteropServices;
using Microsoft.Isam.Esent.Interop.Vista;
using Microsoft.Isam.Esent.Interop.Windows8;
///
/// Windows8 calls to the ESENT interop layer. These calls take the managed types (e.g. JET_SESID) and
/// return errors.
///
internal sealed partial class JetApi : IJetApi
{
#region Transactions
///
/// Causes a session to enter a transaction or create a new save point in an existing
/// transaction.
///
/// The session to begin the transaction for.
/// An optional identifier supplied by the user for identifying the transaction.
/// Transaction options.
/// An error if the call fails.
public int JetBeginTransaction3(JET_SESID sesid, long userTransactionId, BeginTransactionGrbit grbit)
{
TraceFunctionCall();
return Err(NativeMethods.JetBeginTransaction3(sesid.Value, userTransactionId, unchecked((uint)grbit)));
}
///
/// Commits the changes made to the state of the database during the current save point
/// and migrates them to the previous save point. If the outermost save point is committed
/// then the changes made during that save point will be committed to the state of the
/// database and the session will exit the transaction.
///
/// The session to commit the transaction for.
/// Commit options.
/// Duration to commit lazy transaction.
/// Commit-id associated with this commit record.
/// An error if the call fails.
public int JetCommitTransaction2(JET_SESID sesid, CommitTransactionGrbit grbit, TimeSpan durableCommit, out JET_COMMIT_ID commitId)
{
TraceFunctionCall();
this.CheckSupportsWindows8Features("JetCommitTransaction2");
int err;
uint cmsecDurableCommit = (uint)durableCommit.TotalMilliseconds;
NATIVE_COMMIT_ID nativeCommitId = new NATIVE_COMMIT_ID();
unsafe
{
err = Err(NativeMethods.JetCommitTransaction2(sesid.Value, unchecked((uint)grbit), cmsecDurableCommit, ref nativeCommitId));
}
commitId = new JET_COMMIT_ID(nativeCommitId);
return err;
}
#endregion
///
/// Gets extended information about an error.
///
/// The error code about which to retrieve information.
/// Information about the specified error code.
/// An error code.
public int JetGetErrorInfo(
JET_err error,
out JET_ERRINFOBASIC errinfo)
{
TraceFunctionCall();
this.CheckSupportsWindows8Features("JetGetErrorInfo");
NATIVE_ERRINFOBASIC nativeErrinfobasic = new NATIVE_ERRINFOBASIC();
errinfo = new JET_ERRINFOBASIC();
nativeErrinfobasic.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_ERRINFOBASIC)));
int nativeErr = (int)error;
int err = Implementation.NativeMethods.JetGetErrorInfoW(
ref nativeErr,
ref nativeErrinfobasic,
nativeErrinfobasic.cbStruct,
(uint)JET_ErrorInfo.SpecificErr,
(uint)ErrorInfoGrbit.None);
errinfo.SetFromNative(ref nativeErrinfobasic);
return err;
}
///
/// Resizes a currently open database.
///
/// The session to use.
/// The database to grow.
/// The desired size of the database, in pages.
/// The size of the database, in pages, after the call.
/// Resize options.
/// An error code.
public int JetResizeDatabase(
JET_SESID sesid,
JET_DBID dbid,
int desiredPages,
out int actualPages,
ResizeDatabaseGrbit grbit)
{
TraceFunctionCall();
this.CheckSupportsWindows8Features("JetResizeDatabase");
CheckNotNegative(desiredPages, "desiredPages");
uint actualPagesNative = 0;
int err = Err(NativeMethods.JetResizeDatabase(
sesid.Value, dbid.Value, checked((uint)desiredPages), out actualPagesNative, (uint)grbit));
actualPages = checked((int)actualPagesNative);
return err;
}
///
/// Creates indexes over data in an ESE database.
///
/// The session to use.
/// The table to create the index on.
/// Array of objects describing the indexes to be created.
/// Number of index description objects.
///
///
/// and
/// are very similar, and appear to take the same arguments. The difference is in the
/// implementation. JetCreateIndex2 uses LCIDs for Unicode indices (e.g. 1033), while
/// JetCreateIndex4 uses Locale Names (e.g. "en-US" or "de-DE". LCIDs are older, and not as well
/// supported in newer version of windows.
///
///
/// An error code.
///
///
public int JetCreateIndex4(
JET_SESID sesid,
JET_TABLEID tableid,
JET_INDEXCREATE[] indexcreates,
int numIndexCreates)
{
TraceFunctionCall();
this.CheckSupportsWindows8Features("JetCreateIndex4");
CheckNotNull(indexcreates, "indexcreates");
CheckNotNegative(numIndexCreates, "numIndexCreates");
if (numIndexCreates > indexcreates.Length)
{
throw new ArgumentOutOfRangeException(
"numIndexCreates", numIndexCreates, "numIndexCreates is larger than the number of indexes passed in");
}
return CreateIndexes3(sesid, tableid, indexcreates, numIndexCreates);
}
///
/// Creates a temporary table with a single index. A temporary table
/// stores and retrieves records just like an ordinary table created
/// using JetCreateTableColumnIndex. However, temporary tables are
/// much faster than ordinary tables due to their volatile nature.
/// They can also be used to very quickly sort and perform duplicate
/// removal on record sets when accessed in a purely sequential manner.
///
/// The session to use.
///
/// Description of the temporary table to create on input. After a
/// successful call, the structure contains the handle to the temporary
/// table and column identifications.
///
/// An error code.
public int JetOpenTemporaryTable2(JET_SESID sesid, JET_OPENTEMPORARYTABLE temporarytable)
{
TraceFunctionCall();
this.CheckSupportsWindows8Features("JetOpenTemporaryTable2");
CheckNotNull(temporarytable, "temporarytable");
NATIVE_OPENTEMPORARYTABLE2 nativetemporarytable = temporarytable.GetNativeOpenTemporaryTable2();
var nativecolumnids = new uint[nativetemporarytable.ccolumn];
NATIVE_COLUMNDEF[] nativecolumndefs = GetNativecolumndefs(temporarytable.prgcolumndef, temporarytable.ccolumn);
unsafe
{
using (var gchandlecollection = new GCHandleCollection())
{
// Pin memory
nativetemporarytable.prgcolumndef = (NATIVE_COLUMNDEF*)gchandlecollection.Add(nativecolumndefs);
nativetemporarytable.rgcolumnid = (uint*)gchandlecollection.Add(nativecolumnids);
if (null != temporarytable.pidxunicode)
{
NATIVE_UNICODEINDEX2 unicode = temporarytable.pidxunicode.GetNativeUnicodeIndex2();
unicode.szLocaleName = gchandlecollection.Add(Util.ConvertToNullTerminatedUnicodeByteArray(temporarytable.pidxunicode.GetEffectiveLocaleName()));
nativetemporarytable.pidxunicode = (NATIVE_UNICODEINDEX2*)gchandlecollection.Add(unicode);
}
// Call the interop method
int err = Err(NativeMethods.JetOpenTemporaryTable2(sesid.Value, ref nativetemporarytable));
// Convert the return values
SetColumnids(temporarytable.prgcolumndef, temporarytable.prgcolumnid, nativecolumnids, temporarytable.ccolumn);
temporarytable.tableid = new JET_TABLEID { Value = nativetemporarytable.tableid };
return err;
}
}
}
///
/// Creates a table, adds columns, and indices on that table.
///
/// The session to use.
/// The database to which to add the new table.
/// Object describing the table to create.
/// An error if the call fails.
public int JetCreateTableColumnIndex4(
JET_SESID sesid,
JET_DBID dbid,
JET_TABLECREATE tablecreate)
{
TraceFunctionCall();
this.CheckSupportsWindows8Features("JetCreateTableColumnIndex4");
CheckNotNull(tablecreate, "tablecreate");
return CreateTableColumnIndex4(sesid, dbid, tablecreate);
}
#region Session Parameters
///
/// Gets a parameter on the provided session state, used for the lifetime of this session or until reset.
///
/// The session to set the parameter on.
/// The ID of the session parameter to set, see
/// and .
/// A 32-bit integer to retrieve.
/// An error if the call fails.
public int JetGetSessionParameter(
JET_SESID sesid,
JET_sesparam sesparamid,
out int value)
{
TraceFunctionCall();
this.CheckSupportsWindows8Features("JetGetSessionParameter");
int err;
int actualDataSize;
err = NativeMethods.JetGetSessionParameter(
sesid.Value,
(uint)sesparamid,
out value,
sizeof(int),
out actualDataSize);
if (err >= (int)JET_err.Success)
{
if (actualDataSize != sizeof(int))
{
throw new ArgumentException(
string.Format(
CultureInfo.InvariantCulture,
"Bad return value. Unexpected data size returned. Expected {0}, but received {1}.",
sizeof(int),
actualDataSize),
"sesparamid");
}
}
return Err(err);
}
///
/// Gets a parameter on the provided session state, used for the lifetime of this session or until reset.
///
/// The session to set the parameter on.
/// The ID of the session parameter to set, see
/// and .
/// A byte array to retrieve.
/// AThe length of the data array.
/// The actual size of the data field.
/// An error if the call fails.
public int JetGetSessionParameter(
JET_SESID sesid,
JET_sesparam sesparamid,
byte[] data,
int length,
out int actualDataSize)
{
TraceFunctionCall();
this.CheckSupportsWindows8Features("JetGetSessionParameter");
CheckDataSize(data, length, "length");
int err;
err = NativeMethods.JetGetSessionParameter(
sesid.Value,
(uint)sesparamid,
data,
length,
out actualDataSize);
return Err(err);
}
///
/// Sets a parameter on the provided session state, used for the lifetime of this session or until reset.
///
/// The session to set the parameter on.
/// The ID of the session parameter to set.
/// A 32-bit integer to set.
/// An error if the call fails.
public int JetSetSessionParameter(
JET_SESID sesid,
JET_sesparam sesparamid,
int valueToSet)
{
TraceFunctionCall();
this.CheckSupportsWindows8Features("JetSetSessionParameter");
int err;
err = NativeMethods.JetSetSessionParameter(sesid.Value, (uint)sesparamid, ref valueToSet, sizeof(int));
return Err(err);
}
///
/// Sets a parameter on the provided session state, used for the lifetime of this session or until reset.
///
/// The session to set the parameter on.
/// The ID of the session parameter to set.
/// Data to set in this session parameter.
/// Size of the data provided.
/// An error if the call fails.
public int JetSetSessionParameter(
JET_SESID sesid,
JET_sesparam sesparamid,
byte[] data,
int dataSize)
{
TraceFunctionCall();
this.CheckSupportsWindows8Features("JetSetSessionParameter");
CheckNotNegative(dataSize, "dataSize");
CheckDataSize(data, dataSize, "dataSize");
int err;
err = NativeMethods.JetSetSessionParameter(sesid.Value, (uint)sesparamid, data, dataSize);
return Err(err);
}
#endregion
#region JetGetIndexInfo overloads
///
/// Retrieves information about indexes on a table.
///
/// The session to use.
/// The database to use.
/// The name of the table to retrieve index information about.
/// The name of the index to retrieve information about.
/// Filled in with information about indexes on the table.
/// The type of information to retrieve.
/// An error if the call fails.
public int JetGetIndexInfo(
JET_SESID sesid,
JET_DBID dbid,
string tablename,
string indexname,
out JET_INDEXCREATE result,
JET_IdxInfo infoLevel)
{
TraceFunctionCall();
CheckNotNull(tablename, "tablename");
int err;
switch (infoLevel)
{
case Microsoft.Isam.Esent.Interop.Windows7.Windows7IdxInfo.CreateIndex:
case Microsoft.Isam.Esent.Interop.Windows7.Windows7IdxInfo.CreateIndex2:
case Microsoft.Isam.Esent.Interop.Windows8.Windows8IdxInfo.InfoCreateIndex3:
break;
default:
throw new ArgumentException(string.Format("{0} is not a valid value JET_IdxInfo for this JET_INDEXCREATE overload."));
}
if (this.Capabilities.SupportsWindows8Features)
{
{
int bufferSize = 10 * Marshal.SizeOf(typeof(NATIVE_INDEXCREATE3));
IntPtr unmanagedBuffer = Marshal.AllocHGlobal(bufferSize);
try
{
// var nativeIndexcreate = new NATIVE_INDEXCREATE3();
// nativeIndexcreate.cbStruct = checked((uint)bufferSize);
infoLevel = Windows8IdxInfo.InfoCreateIndex3;
err = Err(NativeMethods.JetGetIndexInfoW(
sesid.Value,
dbid.Value,
tablename,
indexname,
unmanagedBuffer,
(uint)bufferSize,
(uint)infoLevel));
NATIVE_INDEXCREATE3 nativeIndexcreate = (NATIVE_INDEXCREATE3)Marshal.PtrToStructure(unmanagedBuffer, typeof(NATIVE_INDEXCREATE3));
result = new JET_INDEXCREATE();
result.SetAllFromNativeIndexCreate(ref nativeIndexcreate);
}
finally
{
Marshal.FreeHGlobal(unmanagedBuffer);
}
}
}
else
{
result = null;
err = Err((int)JET_err.FeatureNotAvailable);
}
return err;
}
///
/// Retrieves information about indexes on a table.
///
/// The session to use.
/// The table to retrieve index information about.
/// The name of the index to retrieve information about.
/// Filled in with information about indexes on the table.
/// The type of information to retrieve.
/// An error if the call fails.
public int JetGetTableIndexInfo(
JET_SESID sesid,
JET_TABLEID tableid,
string indexname,
out JET_INDEXCREATE result,
JET_IdxInfo infoLevel)
{
TraceFunctionCall();
int err;
switch (infoLevel)
{
case Microsoft.Isam.Esent.Interop.Windows7.Windows7IdxInfo.CreateIndex:
case Microsoft.Isam.Esent.Interop.Windows7.Windows7IdxInfo.CreateIndex2:
case Microsoft.Isam.Esent.Interop.Windows8.Windows8IdxInfo.InfoCreateIndex3:
break;
default:
throw new ArgumentException(string.Format("{0} is not a valid value JET_IdxInfo for this JET_INDEXCREATE overload."));
}
if (this.Capabilities.SupportsWindows8Features)
{
{
int bufferSize = 10 * Marshal.SizeOf(typeof(NATIVE_INDEXCREATE3));
IntPtr unmanagedBuffer = Marshal.AllocHGlobal(bufferSize);
try
{
// var nativeIndexcreate = new NATIVE_INDEXCREATE3();
// nativeIndexcreate.cbStruct = checked((uint)bufferSize);
infoLevel = Windows8IdxInfo.InfoCreateIndex3;
err = Err(NativeMethods.JetGetTableIndexInfoW(
sesid.Value,
tableid.Value,
indexname,
unmanagedBuffer,
(uint)bufferSize,
(uint)infoLevel));
NATIVE_INDEXCREATE3 nativeIndexcreate = (NATIVE_INDEXCREATE3)Marshal.PtrToStructure(unmanagedBuffer, typeof(NATIVE_INDEXCREATE3));
result = new JET_INDEXCREATE();
result.SetAllFromNativeIndexCreate(ref nativeIndexcreate);
}
finally
{
Marshal.FreeHGlobal(unmanagedBuffer);
}
}
}
else
{
result = null;
err = Err((int)JET_err.FeatureNotAvailable);
}
return err;
}
#endregion
#region prereading
///
/// If the records with the specified key rangess are not in the buffer
/// cache, then start asynchronous reads to bring the records into the
/// database buffer cache.
///
/// The session to use.
/// The table to issue the prereads against.
/// The key ranges to preread.
/// The index of the first key range in the array to read.
/// The maximum number of key ranges to preread.
/// Returns the number of keys actually preread.
/// List of column ids for long value columns to preread.
/// Preread options. Used to specify the direction of the preread.
///
/// An error if the call fails.
///
public int JetPrereadIndexRanges(
JET_SESID sesid,
JET_TABLEID tableid,
JET_INDEX_RANGE[] indexRanges,
int rangeIndex,
int rangeCount,
out int rangesPreread,
JET_COLUMNID[] columnsPreread,
PrereadIndexRangesGrbit grbit)
{
TraceFunctionCall();
this.CheckSupportsWindows8Features("JetPrereadIndexRanges");
CheckNotNull(indexRanges, "indexRanges");
CheckDataSize(indexRanges, rangeIndex, "rangeIndex", rangeCount, "rangeCount");
var handles = new GCHandleCollection();
try
{
NATIVE_INDEX_RANGE[] nativeRanges = new NATIVE_INDEX_RANGE[rangeCount];
for (int i = 0; i < rangeCount; i++)
{
nativeRanges[i] = indexRanges[i + rangeIndex].GetNativeIndexRange(ref handles);
}
if (columnsPreread != null)
{
var nativecolumnids = new uint[columnsPreread.Length];
for (int i = 0; i < columnsPreread.Length; i++)
{
nativecolumnids[i] = (uint)columnsPreread[i].Value;
}
return Err(NativeMethods.JetPrereadIndexRanges(sesid.Value, tableid.Value, nativeRanges, (uint)rangeCount, out rangesPreread, nativecolumnids, (uint)columnsPreread.Length, checked((uint)grbit)));
}
else
{
return Err(NativeMethods.JetPrereadIndexRanges(sesid.Value, tableid.Value, nativeRanges, (uint)rangeCount, out rangesPreread, null, (uint)0, checked((uint)grbit)));
}
}
finally
{
handles.Dispose();
}
}
///
/// If the records with the specified key ranges are not in the
/// buffer cache then start asynchronous reads to bring the records
/// into the database buffer cache.
///
/// The session to use.
/// The table to issue the prereads against.
/// The start of key ranges to preread.
/// The lengths of the start keys to preread.
/// The end of key rangess to preread.
/// The lengths of the end keys to preread.
/// The index of the first key range in the array to read.
/// The maximum number of key ranges to preread.
/// Returns the number of keys actually preread.
/// List of column ids for long value columns to preread.
/// Preread options. Used to specify the direction of the preread.
/// An error or warning.
public int JetPrereadKeyRanges(
JET_SESID sesid,
JET_TABLEID tableid,
byte[][] keysStart,
int[] keyStartLengths,
byte[][] keysEnd,
int[] keyEndLengths,
int rangeIndex,
int rangeCount,
out int rangesPreread,
JET_COLUMNID[] columnsPreread,
PrereadIndexRangesGrbit grbit)
{
TraceFunctionCall();
this.CheckSupportsWindows8Features("JetPrereadKeyRanges");
CheckDataSize(keysStart, rangeIndex, "rangeIndex", rangeCount, "rangeCount");
CheckDataSize(keyStartLengths, rangeIndex, "rangeIndex", rangeCount, "rangeCount");
CheckNotNull(keysStart, "keysStart");
if (keysEnd != null)
{
CheckNotNull(keyEndLengths, "keyEndLengths");
CheckDataSize(keysEnd, rangeIndex, "rangeIndex", rangeCount, "rangeCount");
}
if (keyEndLengths != null)
{
CheckNotNull(keysEnd, "keysEnd");
CheckDataSize(keyEndLengths, rangeIndex, "rangeIndex", rangeCount, "rangeCount");
}
grbit = grbit | PrereadIndexRangesGrbit.NormalizedKey;
using (var handles = new GCHandleCollection())
{
NATIVE_INDEX_COLUMN[] startColumn;
NATIVE_INDEX_COLUMN[] endColumn;
NATIVE_INDEX_RANGE[] ranges = new NATIVE_INDEX_RANGE[rangeCount];
for (int i = 0; i < rangeCount; i++)
{
startColumn = new NATIVE_INDEX_COLUMN[1];
startColumn[0].pvData = handles.Add(keysStart[i + rangeIndex]);
startColumn[0].cbData = (uint)keyStartLengths[i + rangeIndex];
ranges[i].rgStartColumns = handles.Add(startColumn);
ranges[i].cStartColumns = 1;
if (keysEnd != null)
{
endColumn = new NATIVE_INDEX_COLUMN[1];
endColumn[0].pvData = handles.Add(keysEnd[i + rangeIndex]);
endColumn[0].cbData = (uint)keyEndLengths[i + rangeIndex];
ranges[i].rgEndColumns = handles.Add(endColumn);
ranges[i].cEndColumns = 1;
}
}
if (columnsPreread != null)
{
var nativecolumnids = new uint[columnsPreread.Length];
for (int i = 0; i < columnsPreread.Length; i++)
{
nativecolumnids[i] = (uint)columnsPreread[i].Value;
}
return Err(NativeMethods.JetPrereadIndexRanges(sesid.Value, tableid.Value, ranges, (uint)rangeCount, out rangesPreread, nativecolumnids, (uint)columnsPreread.Length, checked((uint)grbit)));
}
else
{
return Err(NativeMethods.JetPrereadIndexRanges(sesid.Value, tableid.Value, ranges, (uint)rangeCount, out rangesPreread, null, (uint)0, checked((uint)grbit)));
}
}
}
///
/// Set an array of simple filters for
///
/// The session to use for the call.
/// The cursor to position.
/// Simple record filters.
/// Move options.
/// An error if the call fails.
public int JetSetCursorFilter(JET_SESID sesid, JET_TABLEID tableid, JET_INDEX_COLUMN[] filters, CursorFilterGrbit grbit)
{
TraceFunctionCall();
this.CheckSupportsWindows8Features("JetSetCursorFilter");
if (filters == null || filters.Length == 0)
{
return Err(NativeMethods.JetSetCursorFilter(sesid.Value, tableid.Value, null, 0, checked((uint)grbit)));
}
var handles = new GCHandleCollection();
try
{
NATIVE_INDEX_COLUMN[] nativeFilters = new NATIVE_INDEX_COLUMN[filters.Length];
for (int i = 0; i < filters.Length; i++)
{
nativeFilters[i] = filters[i].GetNativeIndexColumn(ref handles);
}
return Err(NativeMethods.JetSetCursorFilter(sesid.Value, tableid.Value, nativeFilters, (uint)filters.Length, checked((uint)grbit)));
}
finally
{
handles.Dispose();
}
}
#endregion
#region Private utility functions
///
/// Make native indexcreate structures from the managed ones.
///
/// Index create structures to convert.
/// The handle collection used to pin the data.
/// Pinned native versions of the index creates.
private static unsafe NATIVE_INDEXCREATE3[] GetNativeIndexCreate3s(
IList managedIndexCreates,
ref GCHandleCollection handles)
{
NATIVE_INDEXCREATE3[] nativeIndices = null;
if (managedIndexCreates != null && managedIndexCreates.Count > 0)
{
nativeIndices = new NATIVE_INDEXCREATE3[managedIndexCreates.Count];
for (int i = 0; i < managedIndexCreates.Count; ++i)
{
nativeIndices[i] = managedIndexCreates[i].GetNativeIndexcreate3();
if (null != managedIndexCreates[i].pidxUnicode)
{
NATIVE_UNICODEINDEX2 unicode = managedIndexCreates[i].pidxUnicode.GetNativeUnicodeIndex2();
unicode.szLocaleName = handles.Add(Util.ConvertToNullTerminatedUnicodeByteArray(managedIndexCreates[i].pidxUnicode.GetEffectiveLocaleName()));
nativeIndices[i].pidxUnicode = (NATIVE_UNICODEINDEX2*)handles.Add(unicode);
nativeIndices[i].grbit |= (uint)VistaGrbits.IndexUnicode;
}
nativeIndices[i].szKey = handles.Add(Util.ConvertToNullTerminatedUnicodeByteArray(managedIndexCreates[i].szKey));
nativeIndices[i].szIndexName = handles.Add(Util.ConvertToNullTerminatedUnicodeByteArray(managedIndexCreates[i].szIndexName));
nativeIndices[i].rgconditionalcolumn = GetNativeConditionalColumns(managedIndexCreates[i].rgconditionalcolumn, true, ref handles);
// Convert pSpaceHints.
if (managedIndexCreates[i].pSpaceHints != null)
{
NATIVE_SPACEHINTS nativeSpaceHints = managedIndexCreates[i].pSpaceHints.GetNativeSpaceHints();
nativeIndices[i].pSpaceHints = handles.Add(nativeSpaceHints);
}
}
}
return nativeIndices;
}
///
/// Creates indexes over data in an ESE database.
///
/// The session to use.
/// The table to create the index on.
/// Array of objects describing the indexes to be created.
/// Number of index description objects.
/// An error code.
private static int CreateIndexes3(JET_SESID sesid, JET_TABLEID tableid, IList indexcreates, int numIndexCreates)
{
// pin the memory
var handles = new GCHandleCollection();
try
{
NATIVE_INDEXCREATE3[] nativeIndexcreates = GetNativeIndexCreate3s(indexcreates, ref handles);
return Err(NativeMethods.JetCreateIndex4W(sesid.Value, tableid.Value, nativeIndexcreates, checked((uint)numIndexCreates)));
}
finally
{
handles.Dispose();
}
}
///
/// Creates a table, adds columns, and indices on that table.
///
/// The session to use.
/// The database to which to add the new table.
/// Object describing the table to create.
/// An error if the call fails.
private static int CreateTableColumnIndex4(
JET_SESID sesid,
JET_DBID dbid,
JET_TABLECREATE tablecreate)
{
NATIVE_TABLECREATE4 nativeTableCreate = tablecreate.GetNativeTableCreate4();
unsafe
{
var handles = new GCHandleCollection();
try
{
// Convert/pin the column definitions.
nativeTableCreate.rgcolumncreate = (NATIVE_COLUMNCREATE*)GetNativeColumnCreates(tablecreate.rgcolumncreate, true, ref handles);
// Convert/pin the index definitions.
NATIVE_INDEXCREATE3[] nativeIndexCreates = GetNativeIndexCreate3s(tablecreate.rgindexcreate, ref handles);
nativeTableCreate.rgindexcreate = handles.Add(nativeIndexCreates);
// Convert/pin the space hints.
if (tablecreate.pSeqSpacehints != null)
{
NATIVE_SPACEHINTS nativeSpaceHints = tablecreate.pSeqSpacehints.GetNativeSpaceHints();
nativeTableCreate.pSeqSpacehints = (NATIVE_SPACEHINTS*)handles.Add(nativeSpaceHints);
}
if (tablecreate.pLVSpacehints != null)
{
NATIVE_SPACEHINTS nativeSpaceHints = tablecreate.pLVSpacehints.GetNativeSpaceHints();
nativeTableCreate.pLVSpacehints = (NATIVE_SPACEHINTS*)handles.Add(nativeSpaceHints);
}
int err = NativeMethods.JetCreateTableColumnIndex4W(sesid.Value, dbid.Value, ref nativeTableCreate);
// Modified fields.
tablecreate.tableid = new JET_TABLEID
{
Value = nativeTableCreate.tableid
};
tablecreate.cCreated = checked((int)nativeTableCreate.cCreated);
if (tablecreate.rgcolumncreate != null)
{
for (int i = 0; i < tablecreate.rgcolumncreate.Length; ++i)
{
tablecreate.rgcolumncreate[i].SetFromNativeColumnCreate(ref nativeTableCreate.rgcolumncreate[i]);
}
}
if (tablecreate.rgindexcreate != null)
{
for (int i = 0; i < tablecreate.rgindexcreate.Length; ++i)
{
tablecreate.rgindexcreate[i].SetFromNativeIndexCreate(ref nativeIndexCreates[i]);
}
}
return Err(err);
}
finally
{
handles.Dispose();
}
}
}
// Do not add new public functions here, go above private functions above ...
#endregion
}
}