//----------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. // //----------------------------------------------------------------------- namespace Microsoft.Isam.Esent.Interop.Implementation { using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading; using Microsoft.Isam.Esent.Interop.Server2003; using Microsoft.Isam.Esent.Interop.Vista; using Microsoft.Isam.Esent.Interop.Windows7; using Microsoft.Isam.Esent.Interop.Windows8; using Win32 = Microsoft.Isam.Esent.Interop.Win32; /// /// 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 { /// /// API call tracing. /// private static readonly TraceSwitch TraceSwitch = new TraceSwitch("ESENT P/Invoke", "P/Invoke calls to ESENT"); /// /// The version of esent. If this is zero then it is looked up /// with JetGetVersion. /// private readonly uint versionOverride; /// /// Callback wrapper collection. This is used for long-running callbacks /// (callbacks which can be called after the API call returns). Create a /// wrapper here and occasionally clean them up. /// private readonly CallbackWrappers callbackWrappers = new CallbackWrappers(); #if !MANAGEDESENT_ON_WSA // Not exposed in MSDK /// /// Initializes static members of the JetApi class. /// static JetApi() { // Prepare these methods for inclusion in a constrained execution region (CER). // This is needed by the Instance class. Instance accesses these methods virtually // so RemoveUnnecessaryCode won't be able to prepare them. RuntimeHelpers.PrepareMethod(typeof(JetApi).GetMethod("JetCreateInstance").MethodHandle); RuntimeHelpers.PrepareMethod(typeof(JetApi).GetMethod("JetCreateInstance2").MethodHandle); RuntimeHelpers.PrepareMethod(typeof(JetApi).GetMethod("JetInit").MethodHandle); RuntimeHelpers.PrepareMethod(typeof(JetApi).GetMethod("JetInit2").MethodHandle); RuntimeHelpers.PrepareMethod(typeof(JetApi).GetMethod("JetInit3").MethodHandle); RuntimeHelpers.PrepareMethod(typeof(JetApi).GetMethod("JetTerm").MethodHandle); RuntimeHelpers.PrepareMethod(typeof(JetApi).GetMethod("JetTerm2").MethodHandle); } #endif // !MANAGEDESENT_ON_WSA /// /// Initializes a new instance of the JetApi class. This allows the version /// to be set. /// /// /// The version of Esent. This is used to override the results of /// JetGetVersion. /// public JetApi(uint version) { #if MANAGEDESENT_ON_WSA // JetGetVersion isn't available in new Windows UI, so we'll pretend it's always Win8: this.versionOverride = 8250 << 8; #else this.versionOverride = version; #endif // MANAGEDESENT_ON_WSA this.DetermineCapabilities(); } /// /// Initializes a new instance of the JetApi class. /// public JetApi() { #if MANAGEDESENT_ON_WSA // JetGetVersion isn't available in new Windows UI, so we'll pretend it's always Win8: this.versionOverride = 8250 << 8; #endif // MANAGEDESENT_ON_WSA this.DetermineCapabilities(); } /// /// Gets the capabilities of this implementation of ESENT. /// public JetCapabilities Capabilities { get; private set; } #region Init/Term /// /// Allocates a new instance of the database engine. /// /// Returns the new instance. /// The name of the instance. Names must be unique. /// An error if the call fails. public int JetCreateInstance(out JET_INSTANCE instance, string name) { #if MANAGEDESENT_ON_WSA return this.JetCreateInstance2(out instance, name, null, CreateInstanceGrbit.None); #else TraceFunctionCall(); instance.Value = IntPtr.Zero; if (this.Capabilities.SupportsUnicodePaths) { return Err(NativeMethods.JetCreateInstanceW(out instance.Value, name)); } else { return Err(NativeMethods.JetCreateInstance(out instance.Value, name)); } #endif // MANAGEDESENT_ON_WSA } /// /// Allocate a new instance of the database engine for use in a single /// process, with a display name specified. /// /// Returns the newly create instance. /// /// Specifies a unique string identifier for the instance to be created. /// This string must be unique within a given process hosting the /// database engine. /// /// /// A display name for the instance to be created. This will be used /// in eventlog entries. /// /// Creation options. /// An error if the call fails. public int JetCreateInstance2(out JET_INSTANCE instance, string name, string displayName, CreateInstanceGrbit grbit) { TraceFunctionCall(); instance.Value = IntPtr.Zero; #if MANAGEDESENT_ON_WSA return Err(NativeMethods.JetCreateInstance2W(out instance.Value, name, displayName, (uint)grbit)); #else if (this.Capabilities.SupportsUnicodePaths) { return Err(NativeMethods.JetCreateInstance2W(out instance.Value, name, displayName, (uint)grbit)); } else { return Err(NativeMethods.JetCreateInstance2(out instance.Value, name, displayName, (uint)grbit)); } #endif // MANAGEDESENT_ON_WSA } /// /// Initialize the ESENT database engine. /// /// /// The instance to initialize. If an instance hasn't been /// allocated then a new one is created and the engine /// will operate in single-instance mode. /// /// An error if the call fails. public int JetInit(ref JET_INSTANCE instance) { #if MANAGEDESENT_ON_WSA return this.JetInit3(ref instance, null, InitGrbit.None); #else TraceFunctionCall(); return Err(NativeMethods.JetInit(ref instance.Value)); #endif // MANAGEDESENT_ON_WSA } /// /// Initialize the ESENT database engine. /// /// /// The instance to initialize. If an instance hasn't been /// allocated then a new one is created and the engine /// will operate in single-instance mode. /// /// /// Initialization options. /// /// An error or a warning. public int JetInit2(ref JET_INSTANCE instance, InitGrbit grbit) { #if MANAGEDESENT_ON_WSA return this.JetInit3(ref instance, null, grbit); #else TraceFunctionCall(); return Err(NativeMethods.JetInit2(ref instance.Value, (uint)grbit)); #endif // MANAGEDESENT_ON_WSA } /// /// Initialize the ESENT database engine. /// /// /// The instance to initialize. If an instance hasn't been /// allocated then a new one is created and the engine /// will operate in single-instance mode. /// /// /// Additional recovery parameters for remapping databases during /// recovery, position where to stop recovery at, or recovery status. /// /// /// Initialization options. /// /// An error code or warning. public int JetInit3(ref JET_INSTANCE instance, JET_RSTINFO recoveryOptions, InitGrbit grbit) { TraceFunctionCall(); this.CheckSupportsVistaFeatures("JetInit3"); if (null != recoveryOptions) { var callbackWrapper = new StatusCallbackWrapper(recoveryOptions.pfnStatus); NATIVE_RSTINFO rstinfo = recoveryOptions.GetNativeRstinfo(); unsafe { int numMaps = (null == recoveryOptions.rgrstmap) ? 0 : recoveryOptions.rgrstmap.Length; try { NATIVE_RSTMAP* maps = stackalloc NATIVE_RSTMAP[numMaps]; if (numMaps > 0) { rstinfo.rgrstmap = maps; for (int i = 0; i < numMaps; ++i) { rstinfo.rgrstmap[i] = recoveryOptions.rgrstmap[i].GetNativeRstmap(); } } rstinfo.pfnStatus = callbackWrapper.NativeCallback; int err = Err(NativeMethods.JetInit3W(ref instance.Value, ref rstinfo, (uint)grbit)); callbackWrapper.ThrowSavedException(); return err; } finally { if (null != rstinfo.rgrstmap) { for (int i = 0; i < numMaps; ++i) { rstinfo.rgrstmap[i].FreeHGlobal(); } } } } } else { return Err(NativeMethods.JetInit3W(ref instance.Value, IntPtr.Zero, (uint)grbit)); } } #if !MANAGEDESENT_ON_WSA // Not exposed in MSDK /// /// Retrieves information about the instances that are running. /// /// /// Returns the number of instances. /// /// /// Returns an array of instance info objects, one for each running /// instance. /// /// An error code if the call fails. public int JetGetInstanceInfo(out int numInstances, out JET_INSTANCE_INFO[] instances) { TraceFunctionCall(); unsafe { uint nativeNumInstance = 0; // Esent will allocate memory which will be freed by the ConvertInstanceInfos call. NATIVE_INSTANCE_INFO* nativeInstanceInfos = null; int err; if (this.Capabilities.SupportsUnicodePaths) { err = NativeMethods.JetGetInstanceInfoW(out nativeNumInstance, out nativeInstanceInfos); instances = this.ConvertInstanceInfosUnicode(nativeNumInstance, nativeInstanceInfos); } else { err = NativeMethods.JetGetInstanceInfo(out nativeNumInstance, out nativeInstanceInfos); instances = this.ConvertInstanceInfosAscii(nativeNumInstance, nativeInstanceInfos); } numInstances = instances.Length; return Err(err); } } /// /// Retrieves information about an instance. /// /// The instance to get information about. /// Retrieved information. /// The type of information to retrieve. /// An error code if the call fails. public int JetGetInstanceMiscInfo(JET_INSTANCE instance, out JET_SIGNATURE signature, JET_InstanceMiscInfo infoLevel) { TraceFunctionCall(); this.CheckSupportsVistaFeatures("JetGetInstanceMiscInfo"); var nativeSignature = new NATIVE_SIGNATURE(); int err = NativeMethods.JetGetInstanceMiscInfo( instance.Value, ref nativeSignature, checked((uint)NATIVE_SIGNATURE.Size), unchecked((uint)infoLevel)); signature = new JET_SIGNATURE(nativeSignature); return Err(err); } /// /// Prevents streaming backup-related activity from continuing on a /// specific running instance, thus ending the streaming backup in /// a predictable way. /// /// The instance to use. /// An error code. public int JetStopBackupInstance(JET_INSTANCE instance) { TraceFunctionCall(); return Err(NativeMethods.JetStopBackupInstance(instance.Value)); } /// /// Prepares an instance for termination. /// /// The (running) instance to use. /// An error code. public int JetStopServiceInstance(JET_INSTANCE instance) { TraceFunctionCall(); return Err(NativeMethods.JetStopServiceInstance(instance.Value)); } #endif // !MANAGEDESENT_ON_WSA /// /// Prepares an instance for termination. Can also be used to resume a previous quiescing. /// /// The (running) instance to use. /// The options to stop or resume the instance. /// An error code. public int JetStopServiceInstance2( JET_INSTANCE instance, StopServiceGrbit grbit) { TraceFunctionCall(); this.CheckSupportsWindows8Features("JetStopServiceInstance2"); return Err(NativeMethods.JetStopServiceInstance2(instance.Value, unchecked((uint)grbit))); } /// /// Terminate an instance that was created with or /// . /// /// The instance to terminate. /// An error or warning. public int JetTerm(JET_INSTANCE instance) { #if MANAGEDESENT_ON_WSA return this.JetTerm2(instance, TermGrbit.None); #else TraceFunctionCall(); this.callbackWrappers.Collect(); if (!instance.IsInvalid) { return Err(NativeMethods.JetTerm(instance.Value)); } return (int)JET_err.Success; #endif // MANAGEDESENT_ON_WSA } /// /// Terminate an instance that was created with or /// . /// /// The instance to terminate. /// Termination options. /// An error or warning. public int JetTerm2(JET_INSTANCE instance, TermGrbit grbit) { TraceFunctionCall(); this.callbackWrappers.Collect(); if (!instance.IsInvalid) { return Err(NativeMethods.JetTerm2(instance.Value, (uint)grbit)); } return (int)JET_err.Success; } /// /// Sets database configuration options. /// /// /// The instance to set the option on or /// to set the option on all instances. /// /// The session to use. /// The parameter to set. /// The value of the parameter to set, if the parameter is an integer type. /// The value of the parameter to set, if the parameter is a string type. /// An error or warning. public int JetSetSystemParameter(JET_INSTANCE instance, JET_SESID sesid, JET_param paramid, IntPtr paramValue, string paramString) { TraceFunctionCall(); unsafe { IntPtr* pinstance = (IntPtr.Zero == instance.Value) ? null : &instance.Value; if (this.Capabilities.SupportsUnicodePaths) { return Err(NativeMethods.JetSetSystemParameterW(pinstance, sesid.Value, (uint)paramid, paramValue, paramString)); } #if MANAGEDESENT_ON_WSA return Err((int)JET_err.FeatureNotAvailable); #else return Err(NativeMethods.JetSetSystemParameter(pinstance, sesid.Value, (uint)paramid, paramValue, paramString)); #endif // MANAGEDESENT_ON_WSA } } /// /// Sets database configuration options. This overload is used when the /// parameter being set is of type JET_CALLBACK. /// /// /// The instance to set the option on or /// to set the option on all instances. /// /// The session to use. /// The parameter to set. /// The value of the parameter to set. /// The value of the string parameter to set. /// An error or warning. public int JetSetSystemParameter(JET_INSTANCE instance, JET_SESID sesid, JET_param paramid, JET_CALLBACK paramValue, string paramString) { TraceFunctionCall(); unsafe { // We are interested in the callback, not the string so we always use the ASCII API. IntPtr* pinstance = (IntPtr.Zero == instance.Value) ? null : &instance.Value; if (null == paramValue) { return Err( #if MANAGEDESENT_ON_WSA NativeMethods.JetSetSystemParameterW( pinstance, sesid.Value, (uint)paramid, IntPtr.Zero, paramString)); #else NativeMethods.JetSetSystemParameter( pinstance, sesid.Value, (uint)paramid, IntPtr.Zero, paramString)); #endif // MANAGEDESENT_ON_WSA } JetCallbackWrapper wrapper = this.callbackWrappers.Add(paramValue); this.callbackWrappers.Collect(); IntPtr functionPointer = Marshal.GetFunctionPointerForDelegate(wrapper.NativeCallback); #if DEBUG GC.Collect(); #endif // DEBUG #if MANAGEDESENT_ON_WSA return Err( NativeMethods.JetSetSystemParameterW( pinstance, sesid.Value, (uint)paramid, functionPointer, paramString)); #else return Err( NativeMethods.JetSetSystemParameter( pinstance, sesid.Value, (uint)paramid, functionPointer, paramString)); #endif // MANAGEDESENT_ON_WSA } } /// /// Gets database configuration options. /// /// The instance to retrieve the options from. /// The session to use. /// The parameter to get. /// Returns the value of the parameter, if the value is an integer. /// Returns the value of the parameter, if the value is a string. /// The maximum size of the parameter string. /// An ESENT warning code. /// /// passes in the error number in the paramValue, which is why it is /// a ref parameter and not an out parameter. /// /// An error or warning. public int JetGetSystemParameter(JET_INSTANCE instance, JET_SESID sesid, JET_param paramid, ref IntPtr paramValue, out string paramString, int maxParam) { TraceFunctionCall(); CheckNotNegative(maxParam, "maxParam"); uint bytesMax = checked((uint)(this.Capabilities.SupportsUnicodePaths ? maxParam * sizeof(char) : maxParam)); var sb = new StringBuilder(maxParam); int err; if (this.Capabilities.SupportsUnicodePaths) { err = Err(NativeMethods.JetGetSystemParameterW(instance.Value, sesid.Value, (uint)paramid, ref paramValue, sb, bytesMax)); } else { #if MANAGEDESENT_ON_WSA err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetSystemParameter(instance.Value, sesid.Value, (uint)paramid, ref paramValue, sb, bytesMax)); #endif } paramString = sb.ToString(); paramString = StringCache.TryToIntern(paramString); return err; } #if !MANAGEDESENT_ON_WSA // Not exposed in MSDK /// /// Retrieves the version of the database engine. /// /// The session to use. /// Returns the version number of the database engine. /// An error code if the call fails. public int JetGetVersion(JET_SESID sesid, out uint version) { TraceFunctionCall(); uint nativeVersion; int err; if (0 != this.versionOverride) { // We have an explicitly set version Trace.WriteLineIf( TraceSwitch.TraceVerbose, string.Format(CultureInfo.InvariantCulture, "JetGetVersion overridden with 0x{0:X}", this.versionOverride)); nativeVersion = this.versionOverride; err = 0; } else { // Get the version from Esent err = Err(NativeMethods.JetGetVersion(sesid.Value, out nativeVersion)); } version = nativeVersion; return err; } #endif // !MANAGEDESENT_ON_WSA #endregion #region Databases /// /// Creates and attaches a database file. /// /// The session to use. /// The path to the database file to create. /// The parameter is not used. /// Returns the dbid of the new database. /// Database creation options. /// An error or warning. public int JetCreateDatabase(JET_SESID sesid, string database, string connect, out JET_DBID dbid, CreateDatabaseGrbit grbit) { #if MANAGEDESENT_ON_WSA return this.JetCreateDatabase2(sesid, database, 0, out dbid, grbit); #else TraceFunctionCall(); CheckNotNull(database, "database"); dbid = JET_DBID.Nil; if (this.Capabilities.SupportsUnicodePaths) { return Err(NativeMethods.JetCreateDatabaseW(sesid.Value, database, connect, out dbid.Value, (uint)grbit)); } return Err(NativeMethods.JetCreateDatabase(sesid.Value, database, connect, out dbid.Value, (uint)grbit)); #endif // MANAGEDESENT_ON_WSA } /// /// Creates and attaches a database file with a maximum database size specified. /// . /// /// The session to use. /// The path to the database file to create. /// /// The maximum size, in database pages, of the database. Passing 0 means there is /// no enforced maximum. /// /// Returns the dbid of the new database. /// Database creation options. /// An error or warning. public int JetCreateDatabase2(JET_SESID sesid, string database, int maxPages, out JET_DBID dbid, CreateDatabaseGrbit grbit) { TraceFunctionCall(); CheckNotNull(database, "database"); CheckNotNegative(maxPages, "maxPages"); dbid = JET_DBID.Nil; uint cpgDatabaseSizeMax = checked((uint)maxPages); if (this.Capabilities.SupportsUnicodePaths) { return Err(NativeMethods.JetCreateDatabase2W(sesid.Value, database, cpgDatabaseSizeMax, out dbid.Value, (uint)grbit)); } #if MANAGEDESENT_ON_WSA return Err((int)JET_err.FeatureNotAvailable); #else return Err(NativeMethods.JetCreateDatabase2(sesid.Value, database, cpgDatabaseSizeMax, out dbid.Value, (uint)grbit)); #endif } /// /// Attaches a database file for use with a database instance. In order to use the /// database, it will need to be subsequently opened with . /// /// The session to use. /// The database to attach. /// Attach options. /// An error or warning. public int JetAttachDatabase(JET_SESID sesid, string database, AttachDatabaseGrbit grbit) { #if MANAGEDESENT_ON_WSA return this.JetAttachDatabase2(sesid, database, 0, grbit); #else TraceFunctionCall(); CheckNotNull(database, "database"); if (this.Capabilities.SupportsUnicodePaths) { return Err(NativeMethods.JetAttachDatabaseW(sesid.Value, database, (uint)grbit)); } return Err(NativeMethods.JetAttachDatabase(sesid.Value, database, (uint)grbit)); #endif // MANAGEDESENT_ON_WSA } /// /// Attaches a database file for use with a database instance. In order to use the /// database, it will need to be subsequently opened with . /// . /// /// The session to use. /// The database to attach. /// /// The maximum size, in database pages, of the database. Passing 0 means there is /// no enforced maximum. /// /// Attach options. /// An error or warning. public int JetAttachDatabase2(JET_SESID sesid, string database, int maxPages, AttachDatabaseGrbit grbit) { TraceFunctionCall(); CheckNotNull(database, "database"); CheckNotNegative(maxPages, "maxPages"); if (this.Capabilities.SupportsUnicodePaths) { return Err(NativeMethods.JetAttachDatabase2W(sesid.Value, database, checked((uint)maxPages), (uint)grbit)); } #if MANAGEDESENT_ON_WSA return Err((int)JET_err.FeatureNotAvailable); #else return Err(NativeMethods.JetAttachDatabase2(sesid.Value, database, checked((uint)maxPages), (uint)grbit)); #endif } /// /// Opens a database previously attached with , /// for use with a database session. This function can be called multiple times /// for the same database. /// /// The session that is opening the database. /// The database to open. /// Reserved for future use. /// Returns the dbid of the attached database. /// Open database options. /// An error or warning. public int JetOpenDatabase(JET_SESID sesid, string database, string connect, out JET_DBID dbid, OpenDatabaseGrbit grbit) { TraceFunctionCall(); CheckNotNull(database, "database"); dbid = JET_DBID.Nil; if (this.Capabilities.SupportsUnicodePaths) { return Err(NativeMethods.JetOpenDatabaseW(sesid.Value, database, connect, out dbid.Value, (uint)grbit)); } #if MANAGEDESENT_ON_WSA return Err((int)JET_err.FeatureNotAvailable); #else return Err(NativeMethods.JetOpenDatabase(sesid.Value, database, connect, out dbid.Value, (uint)grbit)); #endif } /// /// Closes a database file that was previously opened with or /// created with . /// /// The session to use. /// The database to close. /// Close options. /// An error or warning. public int JetCloseDatabase(JET_SESID sesid, JET_DBID dbid, CloseDatabaseGrbit grbit) { TraceFunctionCall(); return Err(NativeMethods.JetCloseDatabase(sesid.Value, dbid.Value, (uint)grbit)); } /// /// Releases a database file that was previously attached to a database session. /// /// The database session to use. /// The database to detach. /// An error or warning. public int JetDetachDatabase(JET_SESID sesid, string database) { TraceFunctionCall(); #if MANAGEDESENT_ON_WSA return this.JetDetachDatabase2(sesid, database, DetachDatabaseGrbit.None); #else if (this.Capabilities.SupportsUnicodePaths) { return Err(NativeMethods.JetDetachDatabaseW(sesid.Value, database)); } return Err(NativeMethods.JetDetachDatabase(sesid.Value, database)); #endif // MANAGEDESENT_ON_WSA } /// /// Releases a database file that was previously attached to a database session. /// /// The database session to use. /// The database to detach. /// Detach options. /// An error or warning. public int JetDetachDatabase2(JET_SESID sesid, string database, DetachDatabaseGrbit grbit) { TraceFunctionCall(); if (this.Capabilities.SupportsUnicodePaths) { return Err(NativeMethods.JetDetachDatabase2W(sesid.Value, database, (uint)grbit)); } #if MANAGEDESENT_ON_WSA return Err((int)JET_err.FeatureNotAvailable); #else return Err(NativeMethods.JetDetachDatabase2(sesid.Value, database, (uint)grbit)); #endif } #if !MANAGEDESENT_ON_WSA // Not exposed in MSDK /// /// Makes a copy of an existing database. The copy is compacted to a /// state optimal for usage. Data in the copied data will be packed /// according to the measures chosen for the indexes at index create. /// In this way, compacted data may be stored as densely as possible. /// Alternatively, compacted data may reserve space for subsequent /// record growth or index insertions. /// /// The session to use for the call. /// The source database that will be compacted. /// The name to use for the compacted database. /// /// A callback function that can be called periodically through the /// database compact operation to report progress. /// /// /// This parameter is ignored and should be null. /// /// Compact options. /// An error code. public int JetCompact( JET_SESID sesid, string sourceDatabase, string destinationDatabase, JET_PFNSTATUS statusCallback, object ignored, CompactGrbit grbit) { TraceFunctionCall(); CheckNotNull(sourceDatabase, "sourceDatabase"); CheckNotNull(destinationDatabase, "destinationDatabase"); if (null != ignored) { throw new ArgumentException("must be null", "ignored"); } var callbackWrapper = new StatusCallbackWrapper(statusCallback); IntPtr functionPointer = (null == statusCallback) ? IntPtr.Zero : Marshal.GetFunctionPointerForDelegate(callbackWrapper.NativeCallback); #if DEBUG GC.Collect(); #endif int err; if (this.Capabilities.SupportsUnicodePaths) { err = Err(NativeMethods.JetCompactW( sesid.Value, sourceDatabase, destinationDatabase, functionPointer, IntPtr.Zero, (uint)grbit)); } else { err = Err(NativeMethods.JetCompact( sesid.Value, sourceDatabase, destinationDatabase, functionPointer, IntPtr.Zero, (uint)grbit)); } callbackWrapper.ThrowSavedException(); return err; } /// /// Extends the size of a database that is currently open. /// /// 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. /// /// An error if the call fails. public int JetGrowDatabase(JET_SESID sesid, JET_DBID dbid, int desiredPages, out int actualPages) { TraceFunctionCall(); CheckNotNegative(desiredPages, "desiredPages"); uint actualPagesNative = 0; int err = Err(NativeMethods.JetGrowDatabase( sesid.Value, dbid.Value, checked((uint)desiredPages), out actualPagesNative)); actualPages = checked((int)actualPagesNative); return err; } /// /// Extends the size of a database that is currently open. /// /// The session to use. /// The name of the database to grow. /// The desired size of the database, in pages. /// /// The size of the database, in pages, after the call. /// /// An error if the call fails. public int JetSetDatabaseSize(JET_SESID sesid, string database, int desiredPages, out int actualPages) { TraceFunctionCall(); CheckNotNegative(desiredPages, "desiredPages"); CheckNotNull(database, "database"); uint actualPagesNative = 0; int err; if (this.Capabilities.SupportsUnicodePaths) { err = Err(NativeMethods.JetSetDatabaseSizeW( sesid.Value, database, checked((uint)desiredPages), out actualPagesNative)); } else { err = Err(NativeMethods.JetSetDatabaseSize( sesid.Value, database, checked((uint)desiredPages), out actualPagesNative)); } actualPages = checked((int)actualPagesNative); return err; } #endif // !MANAGEDESENT_ON_WSA /// /// Retrieves certain information about the given database. /// /// The session to use. /// The database identifier. /// The value to be retrieved. /// The specific data to retrieve. /// An error if the call fails. public int JetGetDatabaseInfo( JET_SESID sesid, JET_DBID dbid, out int value, JET_DbInfo infoLevel) { TraceFunctionCall(); #if MANAGEDESENT_ON_WSA return Err(NativeMethods.JetGetDatabaseInfoW(sesid.Value, dbid.Value, out value, sizeof(int), (uint)infoLevel)); #else return Err(NativeMethods.JetGetDatabaseInfo(sesid.Value, dbid.Value, out value, sizeof(int), (uint)infoLevel)); #endif } /// /// Retrieves certain information about the given database. /// /// The session to use. /// The database identifier. /// The value to be retrieved. /// The specific data to retrieve. /// An error if the call fails. public int JetGetDatabaseInfo( JET_SESID sesid, JET_DBID dbid, out JET_DBINFOMISC dbinfomisc, JET_DbInfo infoLevel) { TraceFunctionCall(); int err = (int)JET_err.Success; dbinfomisc = null; bool notYetPublishedSupported = false; this.NotYetPublishedGetDbinfomisc(sesid, dbid, ref dbinfomisc, infoLevel, ref notYetPublishedSupported, ref err); if (notYetPublishedSupported) { // The not-yet-published function in the other file set the 'ref' parameters. } else if (this.Capabilities.SupportsWindows7Features) { NATIVE_DBINFOMISC4 native; err = Err(NativeMethods.JetGetDatabaseInfoW( sesid.Value, dbid.Value, out native, (uint)Marshal.SizeOf(typeof(NATIVE_DBINFOMISC4)), (uint)infoLevel)); dbinfomisc = new JET_DBINFOMISC(); dbinfomisc.SetFromNativeDbinfoMisc(ref native); } #if MANAGEDESENT_ON_WSA else { var native = new NATIVE_DBINFOMISC4(); err = Err((int)JET_err.FeatureNotAvailable); dbinfomisc = new JET_DBINFOMISC(); dbinfomisc.SetFromNativeDbinfoMisc(ref native); } #else else if (this.Capabilities.SupportsVistaFeatures) { NATIVE_DBINFOMISC native; err = Err(NativeMethods.JetGetDatabaseInfoW( sesid.Value, dbid.Value, out native, (uint)Marshal.SizeOf(typeof(NATIVE_DBINFOMISC)), (uint)infoLevel)); dbinfomisc = new JET_DBINFOMISC(); dbinfomisc.SetFromNativeDbinfoMisc(ref native); } else { NATIVE_DBINFOMISC native; err = Err(NativeMethods.JetGetDatabaseInfo( sesid.Value, dbid.Value, out native, (uint)Marshal.SizeOf(typeof(NATIVE_DBINFOMISC)), (uint)infoLevel)); dbinfomisc = new JET_DBINFOMISC(); dbinfomisc.SetFromNativeDbinfoMisc(ref native); } #endif // MANAGEDESENT_ON_WSA return err; } /// /// Retrieves certain information about the given database. /// /// The session to use. /// The database identifier. /// The value to be retrieved. /// The specific data to retrieve. /// An error if the call fails. public int JetGetDatabaseInfo( JET_SESID sesid, JET_DBID dbid, out string value, JET_DbInfo infoLevel) { TraceFunctionCall(); int err; const int MaxCharacters = 1024; StringBuilder sb = new StringBuilder(MaxCharacters); if (this.Capabilities.SupportsUnicodePaths) { err = Err(NativeMethods.JetGetDatabaseInfoW(sesid.Value, dbid.Value, sb, MaxCharacters, (uint)infoLevel)); } else { #if MANAGEDESENT_ON_WSA err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetDatabaseInfo(sesid.Value, dbid.Value, sb, MaxCharacters, (uint)infoLevel)); #endif } value = sb.ToString(); return err; } /// /// Retrieves certain information about the given database. /// /// The file name of the database. /// The value to be retrieved. /// The specific data to retrieve. /// An error if the call fails. public int JetGetDatabaseFileInfo( string databaseName, out int value, JET_DbInfo infoLevel) { TraceFunctionCall(); int err; if (this.Capabilities.SupportsUnicodePaths) { err = Err(NativeMethods.JetGetDatabaseFileInfoW(databaseName, out value, sizeof(int), (uint)infoLevel)); } else { #if MANAGEDESENT_ON_WSA err = Err((int)JET_err.FeatureNotAvailable); value = 0; #else err = Err(NativeMethods.JetGetDatabaseFileInfo(databaseName, out value, sizeof(int), (uint)infoLevel)); #endif } return err; } /// /// Retrieves certain information about the given database. /// /// The file name of the database. /// The value to be retrieved. /// The specific data to retrieve. /// An error if the call fails. public int JetGetDatabaseFileInfo( string databaseName, out long value, JET_DbInfo infoLevel) { TraceFunctionCall(); int err; if (this.Capabilities.SupportsUnicodePaths) { err = Err(NativeMethods.JetGetDatabaseFileInfoW(databaseName, out value, sizeof(long), (uint)infoLevel)); } else { #if MANAGEDESENT_ON_WSA err = Err((int)JET_err.FeatureNotAvailable); value = 0; #else err = Err(NativeMethods.JetGetDatabaseFileInfo(databaseName, out value, sizeof(long), (uint)infoLevel)); #endif } return err; } /// /// Retrieves certain information about the given database. /// /// The file name of the database. /// The value to be retrieved. /// The specific data to retrieve. /// An error if the call fails. public int JetGetDatabaseFileInfo( string databaseName, out JET_DBINFOMISC dbinfomisc, JET_DbInfo infoLevel) { TraceFunctionCall(); int err = (int)JET_err.Success; dbinfomisc = null; bool notYetPublishedSupported = false; this.NotYetPublishedGetDbinfomisc(databaseName, ref dbinfomisc, infoLevel, ref notYetPublishedSupported, ref err); if (notYetPublishedSupported) { // The not-yet-published function in the other file set the 'ref' parameters. } else if (this.Capabilities.SupportsWindows7Features) { // Windows7 -> Unicode path support NATIVE_DBINFOMISC4 native; err = Err(NativeMethods.JetGetDatabaseFileInfoW(databaseName, out native, (uint)Marshal.SizeOf(typeof(NATIVE_DBINFOMISC4)), (uint)infoLevel)); dbinfomisc = new JET_DBINFOMISC(); dbinfomisc.SetFromNativeDbinfoMisc(ref native); } #if MANAGEDESENT_ON_WSA else { err = Err((int)JET_err.FeatureNotAvailable); var native = new NATIVE_DBINFOMISC4(); dbinfomisc = new JET_DBINFOMISC(); dbinfomisc.SetFromNativeDbinfoMisc(ref native); } #else else { NATIVE_DBINFOMISC native; if (this.Capabilities.SupportsUnicodePaths) { err = Err(NativeMethods.JetGetDatabaseFileInfoW(databaseName, out native, (uint)Marshal.SizeOf(typeof(NATIVE_DBINFOMISC)), (uint)infoLevel)); } else { err = Err(NativeMethods.JetGetDatabaseFileInfo(databaseName, out native, (uint)Marshal.SizeOf(typeof(NATIVE_DBINFOMISC)), (uint)infoLevel)); } dbinfomisc = new JET_DBINFOMISC(); dbinfomisc.SetFromNativeDbinfoMisc(ref native); } #endif // MANAGEDESENT_ON_WSA return err; } #endregion #region Backup/Restore #if !MANAGEDESENT_ON_WSA /// /// Performs a streaming backup of an instance, including all the attached /// databases, to a directory. With multiple backup methods supported by /// the engine, this is the simplest and most encapsulated function. /// /// The instance to backup. /// /// The directory where the backup is to be stored. If the backup path is /// null to use the function will truncate the logs, if possible. /// /// Backup options. /// /// Optional status notification callback. /// /// An error code. public int JetBackupInstance( JET_INSTANCE instance, string destination, BackupGrbit grbit, JET_PFNSTATUS statusCallback) { TraceFunctionCall(); var callbackWrapper = new StatusCallbackWrapper(statusCallback); IntPtr functionPointer = (null == statusCallback) ? IntPtr.Zero : Marshal.GetFunctionPointerForDelegate(callbackWrapper.NativeCallback); #if DEBUG GC.Collect(); #endif int err; if (this.Capabilities.SupportsUnicodePaths) { err = Err(NativeMethods.JetBackupInstanceW(instance.Value, destination, (uint)grbit, functionPointer)); } else { err = Err(NativeMethods.JetBackupInstance(instance.Value, destination, (uint)grbit, functionPointer)); } callbackWrapper.ThrowSavedException(); return err; } /// /// Restores and recovers a streaming backup of an instance including all /// the attached databases. It is designed to work with a backup created /// with the function. This is the /// simplest and most encapsulated restore function. /// /// The instance to use. /// /// Location of the backup. The backup should have been created with /// . /// /// /// Name of the folder where the database files from the backup set will /// be copied and recovered. If this is set to null, the database files /// will be copied and recovered to their original location. /// /// /// Optional status notification callback. /// /// An error code. public int JetRestoreInstance(JET_INSTANCE instance, string source, string destination, JET_PFNSTATUS statusCallback) { TraceFunctionCall(); CheckNotNull(source, "source"); var callbackWrapper = new StatusCallbackWrapper(statusCallback); IntPtr functionPointer = (null == statusCallback) ? IntPtr.Zero : Marshal.GetFunctionPointerForDelegate(callbackWrapper.NativeCallback); #if DEBUG GC.Collect(); #endif int err; if (this.Capabilities.SupportsUnicodePaths) { err = Err(NativeMethods.JetRestoreInstanceW(instance.Value, source, destination, functionPointer)); } else { err = Err(NativeMethods.JetRestoreInstance(instance.Value, source, destination, functionPointer)); } callbackWrapper.ThrowSavedException(); return err; } #endif // !MANAGEDESENT_ON_WSA #endregion #region Snapshot Backup #if !MANAGEDESENT_ON_WSA /// /// Begins the preparations for a snapshot session. A snapshot session /// is a short time interval in which the engine does not issue any /// write IOs to disk, so that the engine can participate in a volume /// snapshot session (when driven by a snapshot writer). /// /// Returns the ID of the snapshot session. /// Snapshot options. /// An error code if the call fails. public int JetOSSnapshotPrepare(out JET_OSSNAPID snapid, SnapshotPrepareGrbit grbit) { TraceFunctionCall(); snapid = JET_OSSNAPID.Nil; return Err(NativeMethods.JetOSSnapshotPrepare(out snapid.Value, (uint)grbit)); } /// /// Selects a specific instance to be part of the snapshot session. /// /// The snapshot identifier. /// The instance to add to the snapshot. /// Options for this call. /// An error code if the call fails. public int JetOSSnapshotPrepareInstance(JET_OSSNAPID snapshot, JET_INSTANCE instance, SnapshotPrepareInstanceGrbit grbit) { TraceFunctionCall(); this.CheckSupportsVistaFeatures("JetOSSnapshotPrepareInstance"); return Err(NativeMethods.JetOSSnapshotPrepareInstance(snapshot.Value, instance.Value, (uint)grbit)); } /// /// Starts a snapshot. While the snapshot is in progress, no /// write-to-disk activity by the engine can take place. /// /// The snapshot session. /// /// Returns the number of instances that are part of the snapshot session. /// /// /// Returns information about the instances that are part of the snapshot session. /// /// /// Snapshot freeze options. /// /// An error code if the call fails. public int JetOSSnapshotFreeze(JET_OSSNAPID snapshot, out int numInstances, out JET_INSTANCE_INFO[] instances, SnapshotFreezeGrbit grbit) { TraceFunctionCall(); unsafe { uint nativeNumInstance = 0; NATIVE_INSTANCE_INFO* nativeInstanceInfos = null; int err; if (this.Capabilities.SupportsUnicodePaths) { err = NativeMethods.JetOSSnapshotFreezeW(snapshot.Value, out nativeNumInstance, out nativeInstanceInfos, (uint)grbit); instances = this.ConvertInstanceInfosUnicode(nativeNumInstance, nativeInstanceInfos); } else { err = NativeMethods.JetOSSnapshotFreeze(snapshot.Value, out nativeNumInstance, out nativeInstanceInfos, (uint)grbit); instances = this.ConvertInstanceInfosAscii(nativeNumInstance, nativeInstanceInfos); } numInstances = instances.Length; return Err(err); } } /// /// Retrieves the list of instances and databases that are part of the /// snapshot session at any given moment. /// /// The identifier of the snapshot session. /// Returns the number of instances. /// Returns information about the instances. /// Options for this call. /// An error code if the call fails. public int JetOSSnapshotGetFreezeInfo( JET_OSSNAPID snapshot, out int numInstances, out JET_INSTANCE_INFO[] instances, SnapshotGetFreezeInfoGrbit grbit) { TraceFunctionCall(); this.CheckSupportsVistaFeatures("JetOSSnapshotGetFreezeInfo"); Debug.Assert(this.Capabilities.SupportsUnicodePaths, "JetOSSnapshotGetFreezeInfo is always Unicode"); unsafe { uint nativeNumInstance = 0; NATIVE_INSTANCE_INFO* nativeInstanceInfos = null; int err = NativeMethods.JetOSSnapshotGetFreezeInfoW(snapshot.Value, out nativeNumInstance, out nativeInstanceInfos, (uint)grbit); instances = this.ConvertInstanceInfosUnicode(nativeNumInstance, nativeInstanceInfos); numInstances = instances.Length; return Err(err); } } /// /// Notifies the engine that it can resume normal IO operations after a /// freeze period and a successful snapshot. /// /// The ID of the snapshot. /// Thaw options. /// An error code if the call fails. public int JetOSSnapshotThaw(JET_OSSNAPID snapid, SnapshotThawGrbit grbit) { TraceFunctionCall(); return Err(NativeMethods.JetOSSnapshotThaw(snapid.Value, (uint)grbit)); } /// /// Enables log truncation for all instances that are part of the snapshot session. /// /// /// This function should be called only if the snapshot was created with the /// option. Otherwise, the snapshot /// session ends after the call to . /// /// The snapshot identifier. /// Options for this call. /// An error code if the call fails. public int JetOSSnapshotTruncateLog(JET_OSSNAPID snapshot, SnapshotTruncateLogGrbit grbit) { TraceFunctionCall(); this.CheckSupportsVistaFeatures("JetOSSnapshotTruncateLog"); return Err(NativeMethods.JetOSSnapshotTruncateLog(snapshot.Value, (uint)grbit)); } /// /// Truncates the log for a specified instance during a snapshot session. /// /// /// This function should be called only if the snapshot was created with the /// option. Otherwise, the snapshot /// session ends after the call to . /// /// The snapshot identifier. /// The instance to truncat the log for. /// Options for this call. /// An error code if the call fails. public int JetOSSnapshotTruncateLogInstance(JET_OSSNAPID snapshot, JET_INSTANCE instance, SnapshotTruncateLogGrbit grbit) { TraceFunctionCall(); this.CheckSupportsVistaFeatures("JetOSSnapshotTruncateLogInstance"); return Err(NativeMethods.JetOSSnapshotTruncateLogInstance(snapshot.Value, instance.Value, (uint)grbit)); } /// /// Notifies the engine that the snapshot session finished. /// /// The identifier of the snapshot session. /// Snapshot end options. /// An error code. public int JetOSSnapshotEnd(JET_OSSNAPID snapid, SnapshotEndGrbit grbit) { TraceFunctionCall(); this.CheckSupportsVistaFeatures("JetOSSnapshotEnd"); return Err(NativeMethods.JetOSSnapshotEnd(snapid.Value, (uint)grbit)); } /// /// Notifies the engine that it can resume normal IO operations after a /// freeze period ended with a failed snapshot. /// /// Identifier of the snapshot session. /// Options for this call. /// An error code. public int JetOSSnapshotAbort(JET_OSSNAPID snapid, SnapshotAbortGrbit grbit) { TraceFunctionCall(); this.CheckSupportsServer2003Features("JetOSSnapshotAbort"); return Err(NativeMethods.JetOSSnapshotAbort(snapid.Value, (uint)grbit)); } #endif // !MANAGEDESENT_ON_WSA #endregion #region Streaming Backup/Restore #if !MANAGEDESENT_ON_WSA /// /// Initiates an external backup while the engine and database are online and active. /// /// The instance prepare for backup. /// Backup options. /// An error code if the call fails. public int JetBeginExternalBackupInstance(JET_INSTANCE instance, BeginExternalBackupGrbit grbit) { TraceFunctionCall(); return Err(NativeMethods.JetBeginExternalBackupInstance(instance.Value, (uint)grbit)); } /// /// Closes a file that was opened with JetOpenFileInstance after the /// data from that file has been extracted using JetReadFileInstance. /// /// The instance to use. /// The handle to close. /// An error code if the call fails. public int JetCloseFileInstance(JET_INSTANCE instance, JET_HANDLE handle) { TraceFunctionCall(); return Err(NativeMethods.JetCloseFileInstance(instance.Value, handle.Value)); } /// /// Ends an external backup session. This API is the last API in a series /// of APIs that must be called to execute a successful online /// (non-VSS based) backup. /// /// The instance to end the backup for. /// An error code if the call fails. public int JetEndExternalBackupInstance(JET_INSTANCE instance) { TraceFunctionCall(); return Err(NativeMethods.JetEndExternalBackupInstance(instance.Value)); } /// /// Ends an external backup session. This API is the last API in a series /// of APIs that must be called to execute a successful online /// (non-VSS based) backup. /// /// The instance to end the backup for. /// Options that specify how the backup ended. /// An error code if the call fails. public int JetEndExternalBackupInstance2(JET_INSTANCE instance, EndExternalBackupGrbit grbit) { TraceFunctionCall(); return Err(NativeMethods.JetEndExternalBackupInstance2(instance.Value, (uint)grbit)); } /// /// Used during a backup initiated by /// to query an instance for the names of database files that should become part of /// the backup file set. Only databases that are currently attached to the instance /// using will be considered. These files may /// subsequently be opened using and read /// using . /// /// /// It is important to note that this API does not return an error or warning if /// the output buffer is too small to accept the full list of files that should be /// part of the backup file set. /// /// The instance to get the information for. /// /// Returns a list of null terminated strings describing the set of database files /// that should be a part of the backup file set. The list of strings returned in /// this buffer is in the same format as a multi-string used by the registry. Each /// null-terminated string is returned in sequence followed by a final null terminator. /// /// /// Maximum number of characters to retrieve. /// /// /// Actual size of the file list. If this is greater than maxChars /// then the list has been truncated. /// /// An error code if the call fails. public int JetGetAttachInfoInstance(JET_INSTANCE instance, out string files, int maxChars, out int actualChars) { TraceFunctionCall(); CheckNotNegative(maxChars, "maxChars"); // These strings have embedded nulls so we can't use a StringBuilder. int err; if (this.Capabilities.SupportsUnicodePaths) { uint bytesMax = checked((uint)maxChars) * sizeof(char); byte[] szz = new byte[bytesMax]; uint bytesActual = 0; err = Err(NativeMethods.JetGetAttachInfoInstanceW(instance.Value, szz, bytesMax, out bytesActual)); actualChars = checked((int)bytesActual) / sizeof(char); files = Encoding.Unicode.GetString(szz, 0, Math.Min(szz.Length, (int)bytesActual)); } else { uint bytesMax = checked((uint)maxChars); byte[] szz = new byte[bytesMax]; uint bytesActual = 0; err = Err(NativeMethods.JetGetAttachInfoInstance(instance.Value, szz, bytesMax, out bytesActual)); actualChars = checked((int)bytesActual); files = LibraryHelpers.EncodingASCII.GetString(szz, 0, Math.Min(szz.Length, (int)bytesActual)); } return err; } /// /// Used during a backup initiated by /// to query an instance for the names of database patch files and logfiles that /// should become part of the backup file set. These files may subsequently be /// opened using and read using . /// /// /// It is important to note that this API does not return an error or warning if /// the output buffer is too small to accept the full list of files that should be /// part of the backup file set. /// /// The instance to get the information for. /// /// Returns a list of null terminated strings describing the set of database patch files /// and log files that should be a part of the backup file set. The list of strings returned in /// this buffer is in the same format as a multi-string used by the registry. Each /// null-terminated string is returned in sequence followed by a final null terminator. /// /// /// Maximum number of characters to retrieve. /// /// /// Actual size of the file list. If this is greater than maxChars /// then the list has been truncated. /// /// An error code if the call fails. public int JetGetLogInfoInstance(JET_INSTANCE instance, out string files, int maxChars, out int actualChars) { TraceFunctionCall(); CheckNotNegative(maxChars, "maxChars"); // These strings have embedded nulls so we can't use a StringBuilder. int err; if (this.Capabilities.SupportsUnicodePaths) { uint bytesMax = checked((uint)maxChars) * sizeof(char); byte[] szz = new byte[bytesMax]; uint bytesActual = 0; err = Err(NativeMethods.JetGetLogInfoInstanceW(instance.Value, szz, bytesMax, out bytesActual)); actualChars = checked((int)bytesActual) / sizeof(char); files = Encoding.Unicode.GetString(szz, 0, Math.Min(szz.Length, (int)bytesActual)); } else { uint bytesMax = checked((uint)maxChars); byte[] szz = new byte[bytesMax]; uint bytesActual = 0; err = Err(NativeMethods.JetGetLogInfoInstance(instance.Value, szz, bytesMax, out bytesActual)); actualChars = checked((int)bytesActual); files = LibraryHelpers.EncodingASCII.GetString(szz, 0, Math.Min(szz.Length, (int)bytesActual)); } return err; } /// /// Used during a backup initiated by /// to query an instance for the names of the transaction log files that can be safely /// deleted after the backup has successfully completed. /// /// /// It is important to note that this API does not return an error or warning if /// the output buffer is too small to accept the full list of files that should be /// part of the backup file set. /// /// The instance to get the information for. /// /// Returns a list of null terminated strings describing the set of database log files /// that can be safely deleted after the backup completes. The list of strings returned in /// this buffer is in the same format as a multi-string used by the registry. Each /// null-terminated string is returned in sequence followed by a final null terminator. /// /// /// Maximum number of characters to retrieve. /// /// /// Actual size of the file list. If this is greater than maxChars /// then the list has been truncated. /// /// An error code if the call fails. public int JetGetTruncateLogInfoInstance(JET_INSTANCE instance, out string files, int maxChars, out int actualChars) { TraceFunctionCall(); CheckNotNegative(maxChars, "maxChars"); // These strings have embedded nulls so we can't use a StringBuilder. int err; if (this.Capabilities.SupportsUnicodePaths) { uint bytesMax = checked((uint)maxChars) * sizeof(char); byte[] szz = new byte[bytesMax]; uint bytesActual = 0; err = Err(NativeMethods.JetGetTruncateLogInfoInstanceW(instance.Value, szz, bytesMax, out bytesActual)); actualChars = checked((int)bytesActual) / sizeof(char); files = Encoding.Unicode.GetString(szz, 0, Math.Min(szz.Length, (int)bytesActual)); } else { uint bytesMax = checked((uint)maxChars); byte[] szz = new byte[bytesMax]; uint bytesActual = 0; err = Err(NativeMethods.JetGetTruncateLogInfoInstance(instance.Value, szz, bytesMax, out bytesActual)); actualChars = checked((int)bytesActual); files = LibraryHelpers.EncodingASCII.GetString(szz, 0, Math.Min(szz.Length, (int)bytesActual)); } return err; } /// /// Opens an attached database, database patch file, or transaction log /// file of an active instance for the purpose of performing a streaming /// fuzzy backup. The data from these files can subsequently be read /// through the returned handle using JetReadFileInstance. The returned /// handle must be closed using JetCloseFileInstance. An external backup /// of the instance must have been previously initiated using /// JetBeginExternalBackupInstance. /// /// The instance to use. /// The file to open. /// Returns a handle to the file. /// Returns the least significant 32 bits of the file size. /// Returns the most significant 32 bits of the file size. /// An error code if the call fails. public int JetOpenFileInstance(JET_INSTANCE instance, string file, out JET_HANDLE handle, out long fileSizeLow, out long fileSizeHigh) { TraceFunctionCall(); CheckNotNull(file, "file"); handle = JET_HANDLE.Nil; int err; uint nativeFileSizeLow; uint nativeFileSizeHigh; if (this.Capabilities.SupportsUnicodePaths) { err = Err(NativeMethods.JetOpenFileInstanceW( instance.Value, file, out handle.Value, out nativeFileSizeLow, out nativeFileSizeHigh)); } else { err = Err(NativeMethods.JetOpenFileInstance( instance.Value, file, out handle.Value, out nativeFileSizeLow, out nativeFileSizeHigh)); } fileSizeLow = nativeFileSizeLow; fileSizeHigh = nativeFileSizeHigh; return err; } /// /// Retrieves the contents of a file opened with . /// /// The instance to use. /// The file to read from. /// The buffer to read into. /// The size of the buffer. /// Returns the amount of data read into the buffer. /// An error code if the call fails. public int JetReadFileInstance(JET_INSTANCE instance, JET_HANDLE file, byte[] buffer, int bufferSize, out int bytesRead) { TraceFunctionCall(); CheckNotNull(buffer, "buffer"); CheckDataSize(buffer, bufferSize, "bufferSize"); // ESENT requires that the buffer be aligned on a page allocation boundary. // VirtualAlloc is the API used to do that, so we use P/Invoke to call it. IntPtr alignedBuffer = Win32.NativeMethods.VirtualAlloc( IntPtr.Zero, (UIntPtr)bufferSize, (uint)(Win32.AllocationType.MEM_COMMIT | Win32.AllocationType.MEM_RESERVE), (uint)Win32.MemoryProtection.PAGE_READWRITE); Win32.NativeMethods.ThrowExceptionOnNull(alignedBuffer, "VirtualAlloc"); try { uint nativeBytesRead = 0; int err = Err( NativeMethods.JetReadFileInstance( instance.Value, file.Value, alignedBuffer, checked((uint)bufferSize), out nativeBytesRead)); bytesRead = checked((int)nativeBytesRead); // Copy the memory out of the aligned buffer into the user buffer. Marshal.Copy(alignedBuffer, buffer, 0, bytesRead); return err; } finally { bool freeSucceded = Win32.NativeMethods.VirtualFree(alignedBuffer, UIntPtr.Zero, (uint)Win32.FreeType.MEM_RELEASE); Win32.NativeMethods.ThrowExceptionOnFailure(freeSucceded, "VirtualFree"); } } /// /// Used during a backup initiated by JetBeginExternalBackup to delete /// any transaction log files that will no longer be needed once the /// current backup completes successfully. /// /// The instance to truncate. /// An error code if the call fails. public int JetTruncateLogInstance(JET_INSTANCE instance) { TraceFunctionCall(); return Err(NativeMethods.JetTruncateLogInstance(instance.Value)); } #endif // !MANAGEDESENT_ON_WSA #endregion #region Sessions /// /// Initialize a new ESENT session. /// /// The initialized instance to create the session in. /// Returns the created session. /// The parameter is not used. /// The parameter is not used. /// An error if the call fails. public int JetBeginSession(JET_INSTANCE instance, out JET_SESID sesid, string username, string password) { TraceFunctionCall(); sesid = JET_SESID.Nil; #if MANAGEDESENT_ON_WSA return Err(NativeMethods.JetBeginSessionW(instance.Value, out sesid.Value, username, password)); #else return Err(NativeMethods.JetBeginSession(instance.Value, out sesid.Value, username, password)); #endif } /// /// Associates a session with the current thread using the given context /// handle. This association overrides the default engine requirement /// that a transaction for a given session must occur entirely on the /// same thread. /// /// The session to set the context on. /// The context to set. /// An error if the call fails. public int JetSetSessionContext(JET_SESID sesid, IntPtr context) { TraceFunctionCall(); return Err(NativeMethods.JetSetSessionContext(sesid.Value, context)); } /// /// Disassociates a session from the current thread. This should be /// used in conjunction with JetSetSessionContext. /// /// The session to use. /// An error if the call fails. public int JetResetSessionContext(JET_SESID sesid) { TraceFunctionCall(); return Err(NativeMethods.JetResetSessionContext(sesid.Value)); } /// /// Ends a session. /// /// The session to end. /// This parameter is not used. /// An error if the call fails. public int JetEndSession(JET_SESID sesid, EndSessionGrbit grbit) { TraceFunctionCall(); return Err(NativeMethods.JetEndSession(sesid.Value, (uint)grbit)); } #if !MANAGEDESENT_ON_WSA // Not exposed in MSDK /// /// Initialize a new ESE session in the same instance as the given sesid. /// /// The session to duplicate. /// Returns the new session. /// An error if the call fails. public int JetDupSession(JET_SESID sesid, out JET_SESID newSesid) { TraceFunctionCall(); newSesid = JET_SESID.Nil; return Err(NativeMethods.JetDupSession(sesid.Value, out newSesid.Value)); } #endif // !MANAGEDESENT_ON_WSA /// /// Retrieves performance information from the database engine for the /// current thread. Multiple calls can be used to collect statistics /// that reflect the activity of the database engine on this thread /// between those calls. /// /// /// Returns the thread statistics.. /// /// An error code if the operation fails. public int JetGetThreadStats(out JET_THREADSTATS threadstats) { TraceFunctionCall(); this.CheckSupportsVistaFeatures("JetGetThreadStats"); // To speed up the interop we use unsafe code to avoid initializing // the out parameter. We just call the interop code. unsafe { fixed (JET_THREADSTATS* rawJetThreadstats = &threadstats) { return Err(NativeMethods.JetGetThreadStats(rawJetThreadstats, checked((uint)JET_THREADSTATS.Size))); } } } #endregion #region Tables /// /// Opens a cursor on a previously created table. /// /// The database session to use. /// The database to open the table in. /// The name of the table to open. /// The parameter is not used. /// The parameter is not used. /// Table open options. /// Returns the opened table. /// An error if the call fails. public int JetOpenTable(JET_SESID sesid, JET_DBID dbid, string tablename, byte[] parameters, int parametersLength, OpenTableGrbit grbit, out JET_TABLEID tableid) { TraceFunctionCall(); tableid = JET_TABLEID.Nil; CheckNotNull(tablename, "tablename"); CheckDataSize(parameters, parametersLength, "parametersLength"); #if MANAGEDESENT_ON_WSA return Err(NativeMethods.JetOpenTableW(sesid.Value, dbid.Value, tablename, parameters, checked((uint)parametersLength), (uint)grbit, out tableid.Value)); #else return Err(NativeMethods.JetOpenTable(sesid.Value, dbid.Value, tablename, parameters, checked((uint)parametersLength), (uint)grbit, out tableid.Value)); #endif } /// /// Close an open table. /// /// The session which opened the table. /// The table to close. /// An error if the call fails. public int JetCloseTable(JET_SESID sesid, JET_TABLEID tableid) { TraceFunctionCall(); return Err(NativeMethods.JetCloseTable(sesid.Value, tableid.Value)); } #if !MANAGEDESENT_ON_WSA // Not exposed in MSDK /// /// Duplicates an open cursor and returns a handle to the duplicated cursor. /// If the cursor that was duplicated was a read-only cursor then the /// duplicated cursor is also a read-only cursor. /// Any state related to constructing a search key or updating a record is /// not copied into the duplicated cursor. In addition, the location of the /// original cursor is not duplicated into the duplicated cursor. The /// duplicated cursor is always opened on the clustered index and its /// location is always on the first row of the table. /// /// The session to use. /// The cursor to duplicate. /// The duplicated cursor. /// Reserved for future use. /// An error if the call fails. public int JetDupCursor(JET_SESID sesid, JET_TABLEID tableid, out JET_TABLEID newTableid, DupCursorGrbit grbit) { TraceFunctionCall(); newTableid = JET_TABLEID.Nil; return Err(NativeMethods.JetDupCursor(sesid.Value, tableid.Value, out newTableid.Value, (uint)grbit)); } /// /// Walks each index of a table to exactly compute the number of entries /// in an index, and the number of distinct keys in an index. This /// information, together with the number of database pages allocated /// for an index and the current time of the computation is stored in /// index metadata in the database. This data can be subsequently retrieved /// with information operations. /// /// The session to use. /// The table that the statistics will be computed on. /// An error if the call fails. public int JetComputeStats(JET_SESID sesid, JET_TABLEID tableid) { TraceFunctionCall(); return Err(NativeMethods.JetComputeStats(sesid.Value, tableid.Value)); } /// /// Enables the application to associate a context handle known as /// Local Storage with a cursor or the table associated with that /// cursor. This context handle can be used by the application to /// store auxiliary data that is associated with a cursor or table. /// The application is later notified using a runtime callback when /// the context handle must be released. This makes it possible to /// associate dynamically allocated state with a cursor or table. /// /// The session to use. /// The cursor to use. /// The context handle to be associated with the session or cursor. /// Set options. /// An error if the call fails. public int JetSetLS(JET_SESID sesid, JET_TABLEID tableid, JET_LS ls, LsGrbit grbit) { TraceFunctionCall(); return Err(NativeMethods.JetSetLS(sesid.Value, tableid.Value, ls.Value, (uint)grbit)); } /// /// Enables the application to retrieve the context handle known /// as Local Storage that is associated with a cursor or the table /// associated with that cursor. This context handle must have been /// previously set using . JetGetLS can also /// be used to simultaneously fetch the current context handle for /// a cursor or table and reset that context handle. /// /// The session to use. /// The cursor to use. /// Returns the retrieved context handle. /// Retrieve options. /// An error if the call fails. public int JetGetLS(JET_SESID sesid, JET_TABLEID tableid, out JET_LS ls, LsGrbit grbit) { TraceFunctionCall(); IntPtr native; int err = NativeMethods.JetGetLS(sesid.Value, tableid.Value, out native, (uint)grbit); ls = new JET_LS { Value = native }; return Err(err); } /// /// Determine whether an update of the current record of a cursor /// will result in a write conflict, based on the current update /// status of the record. It is possible that a write conflict will /// ultimately be returned even if JetGetCursorInfo returns successfully. /// because another session may update the record before the current /// session is able to update the same record. /// /// The session to use. /// The cursor to check. /// An error if the call fails. public int JetGetCursorInfo(JET_SESID sesid, JET_TABLEID tableid) { TraceFunctionCall(); return Err(NativeMethods.JetGetCursorInfo(sesid.Value, tableid.Value, IntPtr.Zero, 0, 0)); } #endif // !MANAGEDESENT_ON_WSA #endregion #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 error if the call fails. public int JetBeginTransaction(JET_SESID sesid) { TraceFunctionCall(); #if MANAGEDESENT_ON_WSA // 19513 is an arbitrary number. return this.JetBeginTransaction3(sesid, 19513, BeginTransactionGrbit.None); #else return Err(NativeMethods.JetBeginTransaction(sesid.Value)); #endif } /// /// Causes a session to enter a transaction or create a new save point in an existing /// transaction. /// /// The session to begin the transaction for. /// Transaction options. /// An error if the call fails. public int JetBeginTransaction2(JET_SESID sesid, BeginTransactionGrbit grbit) { TraceFunctionCall(); #if MANAGEDESENT_ON_WSA // 33193 is an arbitrary number. return this.JetBeginTransaction3(sesid, 33193, BeginTransactionGrbit.None); #else return Err(NativeMethods.JetBeginTransaction2(sesid.Value, unchecked((uint)grbit))); #endif } /// /// 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. /// An error if the call fails. public int JetCommitTransaction(JET_SESID sesid, CommitTransactionGrbit grbit) { TraceFunctionCall(); #if MANAGEDESENT_ON_WSA JET_COMMIT_ID commitId; return this.JetCommitTransaction2(sesid, grbit, TimeSpan.Zero, out commitId); #else return Err(NativeMethods.JetCommitTransaction(sesid.Value, unchecked((uint)grbit))); #endif } /// /// Undoes the changes made to the state of the database /// and returns to the last save point. JetRollback will also close any cursors /// opened during the save point. If the outermost save point is undone, the /// session will exit the transaction. /// /// The session to rollback the transaction for. /// Rollback options. /// An error if the call fails. public int JetRollback(JET_SESID sesid, RollbackTransactionGrbit grbit) { TraceFunctionCall(); return Err(NativeMethods.JetRollback(sesid.Value, unchecked((uint)grbit))); } #endregion #region DDL /// /// Create an empty table. The newly created table is opened exclusively. /// /// The session to use. /// The database to create the table in. /// The name of the table to create. /// Initial number of pages in the table. /// /// The default density of the table. This is used when doing sequential inserts. /// /// Returns the tableid of the new table. /// An error if the call fails. public int JetCreateTable(JET_SESID sesid, JET_DBID dbid, string table, int pages, int density, out JET_TABLEID tableid) { TraceFunctionCall(); tableid = JET_TABLEID.Nil; CheckNotNull(table, "table"); #if MANAGEDESENT_ON_WSA var tablecreate = new JET_TABLECREATE { szTableName = table, ulPages = pages, ulDensity = density, }; int err = this.JetCreateTableColumnIndex4(sesid, dbid, tablecreate); tableid = tablecreate.tableid; return err; #else return Err(NativeMethods.JetCreateTable(sesid.Value, dbid.Value, table, pages, density, out tableid.Value)); #endif } /// /// Deletes a table from a database. /// /// The session to use. /// The database to delete the table from. /// The name of the table to delete. /// An error if the call fails. public int JetDeleteTable(JET_SESID sesid, JET_DBID dbid, string table) { TraceFunctionCall(); CheckNotNull(table, "table"); #if MANAGEDESENT_ON_WSA return Err(NativeMethods.JetDeleteTableW(sesid.Value, dbid.Value, table)); #else return Err(NativeMethods.JetDeleteTable(sesid.Value, dbid.Value, table)); #endif } /// /// Add a new column to an existing table. /// /// The session to use. /// The table to add the column to. /// The name of the column. /// The definition of the column. /// The default value of the column. /// The size of the default value. /// Returns the columnid of the new column. /// An error if the call fails. public int JetAddColumn(JET_SESID sesid, JET_TABLEID tableid, string column, JET_COLUMNDEF columndef, byte[] defaultValue, int defaultValueSize, out JET_COLUMNID columnid) { TraceFunctionCall(); columnid = JET_COLUMNID.Nil; CheckNotNull(column, "column"); CheckNotNull(columndef, "columndef"); CheckDataSize(defaultValue, defaultValueSize, "defaultValueSize"); NATIVE_COLUMNDEF nativeColumndef = columndef.GetNativeColumndef(); #if MANAGEDESENT_ON_WSA int err = Err(NativeMethods.JetAddColumnW( sesid.Value, tableid.Value, column, ref nativeColumndef, defaultValue, checked((uint)defaultValueSize), out columnid.Value)); #else int err = Err(NativeMethods.JetAddColumn( sesid.Value, tableid.Value, column, ref nativeColumndef, defaultValue, checked((uint)defaultValueSize), out columnid.Value)); #endif // MANAGEDESENT_ON_WSA // esent doesn't actually set the columnid member of the passed in JET_COLUMNDEF, but we will do that here for // completeness. columndef.columnid = new JET_COLUMNID { Value = columnid.Value }; return err; } /// /// Deletes a column from a database table. /// /// The session to use. /// A cursor on the table to delete the column from. /// The name of the column to be deleted. /// An error if the call fails. public int JetDeleteColumn(JET_SESID sesid, JET_TABLEID tableid, string column) { #if MANAGEDESENT_ON_WSA return this.JetDeleteColumn2(sesid, tableid, column, DeleteColumnGrbit.None); #else TraceFunctionCall(); CheckNotNull(column, "column"); return Err(NativeMethods.JetDeleteColumn(sesid.Value, tableid.Value, column)); #endif } /// /// Deletes a column from a database table. /// /// The session to use. /// A cursor on the table to delete the column from. /// The name of the column to be deleted. /// Column deletion options. /// An error if the call fails. public int JetDeleteColumn2(JET_SESID sesid, JET_TABLEID tableid, string column, DeleteColumnGrbit grbit) { TraceFunctionCall(); CheckNotNull(column, "column"); #if MANAGEDESENT_ON_WSA return Err(NativeMethods.JetDeleteColumn2W(sesid.Value, tableid.Value, column, (uint)grbit)); #else return Err(NativeMethods.JetDeleteColumn2(sesid.Value, tableid.Value, column, (uint)grbit)); #endif } /// /// Creates an index over data in an ESE database. An index can be used to locate /// specific data quickly. /// /// The session to use. /// The table to create the index on. /// /// Pointer to a null-terminated string that specifies the name of the index to create. /// /// Index creation options. /// /// Pointer to a double null-terminated string of null-delimited tokens. /// /// /// The length, in characters, of szKey including the two terminating nulls. /// /// Initial B+ tree density. /// An error if the call fails. public int JetCreateIndex( JET_SESID sesid, JET_TABLEID tableid, string indexName, CreateIndexGrbit grbit, string keyDescription, int keyDescriptionLength, int density) { TraceFunctionCall(); #if MANAGEDESENT_ON_WSA // Up-convert to JetCreateIndex2(). JET_INDEXCREATE indexcreate = new JET_INDEXCREATE(); indexcreate.szIndexName = indexName; indexcreate.grbit = grbit; indexcreate.szKey = keyDescription; indexcreate.cbKey = keyDescriptionLength; indexcreate.ulDensity = density; JET_INDEXCREATE[] indexcreates = new JET_INDEXCREATE[] { indexcreate }; return this.JetCreateIndex2(sesid, tableid, indexcreates, indexcreates.Length); #else CheckNotNull(indexName, "indexName"); CheckNotNegative(keyDescriptionLength, "keyDescriptionLength"); CheckNotNegative(density, "density"); if (keyDescriptionLength > checked(keyDescription.Length + 1)) { throw new ArgumentOutOfRangeException( "keyDescriptionLength", keyDescriptionLength, "cannot be greater than keyDescription.Length"); } return Err(NativeMethods.JetCreateIndex( sesid.Value, tableid.Value, indexName, (uint)grbit, keyDescription, checked((uint)keyDescriptionLength), checked((uint)density))); #endif // MANAGEDESENT_ON_WSA } /// /// 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. public int JetCreateIndex2( JET_SESID sesid, JET_TABLEID tableid, JET_INDEXCREATE[] indexcreates, int numIndexCreates) { TraceFunctionCall(); 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"); } #if MANAGEDESENT_ON_WSA // Note that it is actually a bit risky to up-convert to CreateIndexes3(), which is why we don't // do it in the regular case. // Creating the NATIVE_UNICODEINDEX2 structure requires a locale string (not an LCID). If // JetCreateIndex2() is called, then the caller very likely used an LCID (or no locale at all). // If no locale is specified then it's OK. // But we can't convert an LCID to a locale name reliably on Core CLR platforms. // To get our test code working, we have a small hard-coded list of LCID->locale names. return CreateIndexes3(sesid, tableid, indexcreates, numIndexCreates); #else // NOTE: Don't call CreateIndexes3() on Win8. Unlike other APIs, CreateIndexes3() is // not a superset. It requires locale names, and if the user called JetCreateIndex2(), // the input will likely have LCIDs. if (this.Capabilities.SupportsWindows7Features) { return CreateIndexes2(sesid, tableid, indexcreates, numIndexCreates); } if (this.Capabilities.SupportsVistaFeatures) { return CreateIndexes1(sesid, tableid, indexcreates, numIndexCreates); } return CreateIndexes(sesid, tableid, indexcreates, numIndexCreates); #endif // MANAGEDESENT_ON_WSA } /// /// Deletes an index from a database table. /// /// The session to use. /// A cursor on the table to delete the index from. /// The name of the index to be deleted. /// An error if the call fails. public int JetDeleteIndex(JET_SESID sesid, JET_TABLEID tableid, string index) { TraceFunctionCall(); CheckNotNull(index, "index"); #if MANAGEDESENT_ON_WSA return Err(NativeMethods.JetDeleteIndexW(sesid.Value, tableid.Value, index)); #else return Err(NativeMethods.JetDeleteIndex(sesid.Value, tableid.Value, index)); #endif } /// /// 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. /// /// Column definitions for the columns created in the temporary table. /// /// Number of column definitions. /// Table creation options. /// /// Returns the tableid of the temporary table. Closing this tableid /// frees the resources associated with the temporary table. /// /// /// The output buffer that receives the array of column IDs generated /// during the creation of the temporary table. The column IDs in this /// array will exactly correspond to the input array of column definitions. /// As a result, the size of this buffer must correspond to the size of the input array. /// /// An error code. public int JetOpenTempTable( JET_SESID sesid, JET_COLUMNDEF[] columns, int numColumns, TempTableGrbit grbit, out JET_TABLEID tableid, JET_COLUMNID[] columnids) { TraceFunctionCall(); CheckNotNull(columns, "columnns"); CheckDataSize(columns, numColumns, "numColumns"); CheckNotNull(columnids, "columnids"); CheckDataSize(columnids, numColumns, "numColumns"); #if MANAGEDESENT_ON_WSA return this.JetOpenTempTable3(sesid, columns, numColumns, null, grbit, out tableid, columnids); #else tableid = JET_TABLEID.Nil; NATIVE_COLUMNDEF[] nativecolumndefs = GetNativecolumndefs(columns, numColumns); var nativecolumnids = new uint[numColumns]; int err = Err(NativeMethods.JetOpenTempTable( sesid.Value, nativecolumndefs, checked((uint)numColumns), (uint)grbit, out tableid.Value, nativecolumnids)); SetColumnids(columns, columnids, nativecolumnids, numColumns); return err; #endif } #if !MANAGEDESENT_ON_WSA // Not exposed in MSDK /// /// 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. /// /// Column definitions for the columns created in the temporary table. /// /// Number of column definitions. /// /// The locale ID to use to compare any Unicode key column data in the temporary table. /// Any locale may be used as long as the appropriate language pack has been installed /// on the machine. /// /// Table creation options. /// /// Returns the tableid of the temporary table. Closing this tableid /// frees the resources associated with the temporary table. /// /// /// The output buffer that receives the array of column IDs generated /// during the creation of the temporary table. The column IDs in this /// array will exactly correspond to the input array of column definitions. /// As a result, the size of this buffer must correspond to the size of the input array. /// /// An error code. public int JetOpenTempTable2( JET_SESID sesid, JET_COLUMNDEF[] columns, int numColumns, int lcid, TempTableGrbit grbit, out JET_TABLEID tableid, JET_COLUMNID[] columnids) { TraceFunctionCall(); CheckNotNull(columns, "columnns"); CheckDataSize(columns, numColumns, "numColumns"); CheckNotNull(columnids, "columnids"); CheckDataSize(columnids, numColumns, "numColumns"); tableid = JET_TABLEID.Nil; NATIVE_COLUMNDEF[] nativecolumndefs = GetNativecolumndefs(columns, numColumns); var nativecolumnids = new uint[numColumns]; int err = Err(NativeMethods.JetOpenTempTable2( sesid.Value, nativecolumndefs, checked((uint)numColumns), (uint)lcid, (uint)grbit, out tableid.Value, nativecolumnids)); SetColumnids(columns, columnids, nativecolumnids, numColumns); return err; } #endif // !MANAGEDESENT_ON_WSA /// /// 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. /// /// Column definitions for the columns created in the temporary table. /// /// Number of column definitions. /// /// The Locale ID and normalization flags that will be used to compare /// any Unicode key column data in the temporary table. When this /// is not present then the default options are used. /// /// Table creation options. /// /// Returns the tableid of the temporary table. Closing this tableid /// frees the resources associated with the temporary table. /// /// /// The output buffer that receives the array of column IDs generated /// during the creation of the temporary table. The column IDs in this /// array will exactly correspond to the input array of column definitions. /// As a result, the size of this buffer must correspond to the size of the input array. /// /// An error code. public int JetOpenTempTable3( JET_SESID sesid, JET_COLUMNDEF[] columns, int numColumns, JET_UNICODEINDEX unicodeindex, TempTableGrbit grbit, out JET_TABLEID tableid, JET_COLUMNID[] columnids) { TraceFunctionCall(); CheckNotNull(columns, "columnns"); CheckDataSize(columns, numColumns, "numColumns"); CheckNotNull(columnids, "columnids"); CheckDataSize(columnids, numColumns, "numColumns"); tableid = JET_TABLEID.Nil; NATIVE_COLUMNDEF[] nativecolumndefs = GetNativecolumndefs(columns, numColumns); var nativecolumnids = new uint[numColumns]; int err; if (null != unicodeindex) { NATIVE_UNICODEINDEX nativeunicodeindex = unicodeindex.GetNativeUnicodeIndex(); err = Err(NativeMethods.JetOpenTempTable3( sesid.Value, nativecolumndefs, checked((uint)numColumns), ref nativeunicodeindex, (uint)grbit, out tableid.Value, nativecolumnids)); } else { err = Err(NativeMethods.JetOpenTempTable3( sesid.Value, nativecolumndefs, checked((uint)numColumns), IntPtr.Zero, (uint)grbit, out tableid.Value, nativecolumnids)); } SetColumnids(columns, columnids, nativecolumnids, numColumns); return err; } /// /// 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. /// /// /// Introduced in Windows Vista. /// /// 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 JetOpenTemporaryTable(JET_SESID sesid, JET_OPENTEMPORARYTABLE temporarytable) { TraceFunctionCall(); #if MANAGEDESENT_ON_WSA return this.JetOpenTemporaryTable2(sesid, temporarytable); #else this.CheckSupportsVistaFeatures("JetOpenTemporaryTable"); CheckNotNull(temporarytable, "temporarytable"); NATIVE_OPENTEMPORARYTABLE nativetemporarytable = temporarytable.GetNativeOpenTemporaryTable(); 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) { nativetemporarytable.pidxunicode = (NATIVE_UNICODEINDEX*) gchandlecollection.Add(temporarytable.pidxunicode.GetNativeUnicodeIndex()); } // Call the interop method int err = Err(NativeMethods.JetOpenTemporaryTable(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; } } #endif // MANAGEDESENT_ON_WSA } /// /// 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 JetCreateTableColumnIndex3( JET_SESID sesid, JET_DBID dbid, JET_TABLECREATE tablecreate) { TraceFunctionCall(); CheckNotNull(tablecreate, "tablecreate"); #if MANAGEDESENT_ON_WSA return CreateTableColumnIndex4(sesid, dbid, tablecreate); #else if (this.Capabilities.SupportsWindows7Features) { return CreateTableColumnIndex3(sesid, dbid, tablecreate); } return this.CreateTableColumnIndex2(sesid, dbid, tablecreate); #endif } #region JetGetTableColumnInfo overloads /// /// Retrieves information about a table column. /// /// The session to use. /// The table containing the column. /// The name of the column. /// Filled in with information about the column. /// An error if the call fails. public int JetGetTableColumnInfo( JET_SESID sesid, JET_TABLEID tableid, string columnName, out JET_COLUMNDEF columndef) { TraceFunctionCall(); columndef = new JET_COLUMNDEF(); CheckNotNull(columnName, "columnName"); var nativeColumndef = new NATIVE_COLUMNDEF(); nativeColumndef.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_COLUMNDEF))); int err; if (this.Capabilities.SupportsVistaFeatures) { err = Err(NativeMethods.JetGetTableColumnInfoW( sesid.Value, tableid.Value, columnName, ref nativeColumndef, nativeColumndef.cbStruct, (uint)JET_ColInfo.Default)); } else { #if MANAGEDESENT_ON_WSA err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetTableColumnInfo( sesid.Value, tableid.Value, columnName, ref nativeColumndef, nativeColumndef.cbStruct, (uint)JET_ColInfo.Default)); #endif } columndef.SetFromNativeColumndef(nativeColumndef); return err; } /// /// Retrieves information about a table column. /// /// The session to use. /// The table containing the column. /// The columnid of the column. /// Filled in with information about the column. /// An error if the call fails. public int JetGetTableColumnInfo( JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, out JET_COLUMNDEF columndef) { TraceFunctionCall(); columndef = new JET_COLUMNDEF(); int err; var nativeColumndef = new NATIVE_COLUMNDEF(); nativeColumndef.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_COLUMNDEF))); if (this.Capabilities.SupportsVistaFeatures) { err = Err(NativeMethods.JetGetTableColumnInfoW( sesid.Value, tableid.Value, ref columnid.Value, ref nativeColumndef, nativeColumndef.cbStruct, (uint)JET_ColInfo.ByColid)); } else { #if MANAGEDESENT_ON_WSA err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetTableColumnInfo( sesid.Value, tableid.Value, ref columnid.Value, ref nativeColumndef, nativeColumndef.cbStruct, (uint)JET_ColInfo.ByColid)); #endif } columndef.SetFromNativeColumndef(nativeColumndef); return err; } /// /// Retrieves information about a table column, given its and name. /// /// The session to use. /// The table containing the column. /// The name of the column. /// Filled in with information about the column. /// An error if the call fails. public int JetGetTableColumnInfo( JET_SESID sesid, JET_TABLEID tableid, string columnName, out JET_COLUMNBASE columnbase) { TraceFunctionCall(); CheckNotNull(columnName, "columnName"); int err; if (this.Capabilities.SupportsVistaFeatures) { var nativeColumnbase = new NATIVE_COLUMNBASE_WIDE(); nativeColumnbase.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_COLUMNBASE_WIDE))); err = Err(NativeMethods.JetGetTableColumnInfoW( sesid.Value, tableid.Value, columnName, ref nativeColumnbase, nativeColumnbase.cbStruct, (uint)JET_ColInfo.Base)); columnbase = new JET_COLUMNBASE(nativeColumnbase); } else { #if MANAGEDESENT_ON_WSA err = Err((int)JET_err.FeatureNotAvailable); columnbase = null; #else var nativeColumnbase = new NATIVE_COLUMNBASE(); nativeColumnbase.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_COLUMNBASE))); err = Err(NativeMethods.JetGetTableColumnInfo( sesid.Value, tableid.Value, columnName, ref nativeColumnbase, nativeColumnbase.cbStruct, (uint)JET_ColInfo.Base)); columnbase = new JET_COLUMNBASE(nativeColumnbase); #endif } return err; } /// /// Retrieves information about a table column, given its and . /// /// The session to use. /// The table containing the column. /// The columnid of the column. /// Filled in with information about the column. /// An error if the call fails. public int JetGetTableColumnInfo( JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, out JET_COLUMNBASE columnbase) { TraceFunctionCall(); this.CheckSupportsVistaFeatures("JetGetTableColumnInfo"); int err; var nativeColumnbase = new NATIVE_COLUMNBASE_WIDE(); nativeColumnbase.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_COLUMNBASE_WIDE))); err = Err(NativeMethods.JetGetTableColumnInfoW( sesid.Value, tableid.Value, ref columnid.Value, ref nativeColumnbase, nativeColumnbase.cbStruct, (uint)VistaColInfo.BaseByColid)); columnbase = new JET_COLUMNBASE(nativeColumnbase); return err; } /// /// Retrieves information about all columns in the table. /// /// The session to use. /// The table containing the column. /// The parameter is ignored. /// Additional options for JetGetTableColumnInfo. /// Filled in with information about the columns in the table. /// An error if the call fails. public int JetGetTableColumnInfo( JET_SESID sesid, JET_TABLEID tableid, string ignored, ColInfoGrbit grbit, out JET_COLUMNLIST columnlist) { TraceFunctionCall(); columnlist = new JET_COLUMNLIST(); int err; var nativeColumnlist = new NATIVE_COLUMNLIST(); nativeColumnlist.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_COLUMNLIST))); // Technically, this should have worked in Vista. But there was a bug, and // it was fixed after Windows 7. if (this.Capabilities.SupportsWindows8Features) { err = Err(NativeMethods.JetGetTableColumnInfoW( sesid.Value, tableid.Value, ignored, ref nativeColumnlist, nativeColumnlist.cbStruct, (uint)grbit | (uint)JET_ColInfo.List)); } else { #if MANAGEDESENT_ON_WSA err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetTableColumnInfo( sesid.Value, tableid.Value, ignored, ref nativeColumnlist, nativeColumnlist.cbStruct, (uint)grbit | (uint)JET_ColInfo.List)); #endif } columnlist.SetFromNativeColumnlist(nativeColumnlist); return err; } #endregion #region JetGetColumnInfo overloads /// /// Retrieves information about a table column. /// /// The session to use. /// The database that contains the table. /// The name of the table containing the column. /// The name of the column. /// Filled in with information about the column. /// An error if the call fails. public int JetGetColumnInfo( JET_SESID sesid, JET_DBID dbid, string tablename, string columnName, out JET_COLUMNDEF columndef) { TraceFunctionCall(); columndef = new JET_COLUMNDEF(); CheckNotNull(tablename, "tablename"); CheckNotNull(columnName, "columnName"); int err; var nativeColumndef = new NATIVE_COLUMNDEF(); nativeColumndef.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_COLUMNDEF))); // Technically, this should have worked in Vista. But there was a bug, and // it was fixed after Windows 7. if (this.Capabilities.SupportsWindows8Features) { err = Err(NativeMethods.JetGetColumnInfoW( sesid.Value, dbid.Value, tablename, columnName, ref nativeColumndef, nativeColumndef.cbStruct, (uint)JET_ColInfo.Default)); } else { #if MANAGEDESENT_ON_WSA err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetColumnInfo( sesid.Value, dbid.Value, tablename, columnName, ref nativeColumndef, nativeColumndef.cbStruct, (uint)JET_ColInfo.Default)); #endif } columndef.SetFromNativeColumndef(nativeColumndef); return err; } /// /// Retrieves information about all columns in a table. /// /// The session to use. /// The database that contains the table. /// The name of the table containing the column. /// This parameter is ignored. /// Filled in with information about the columns in the table. /// An error if the call fails. public int JetGetColumnInfo( JET_SESID sesid, JET_DBID dbid, string tablename, string ignored, out JET_COLUMNLIST columnlist) { TraceFunctionCall(); columnlist = new JET_COLUMNLIST(); CheckNotNull(tablename, "tablename"); int err; var nativeColumnlist = new NATIVE_COLUMNLIST(); nativeColumnlist.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_COLUMNLIST))); // Technically, this should have worked in Vista. But there was a bug, and // it was fixed after Windows 7. if (this.Capabilities.SupportsWindows8Features) { err = Err(NativeMethods.JetGetColumnInfoW( sesid.Value, dbid.Value, tablename, ignored, ref nativeColumnlist, nativeColumnlist.cbStruct, (uint)JET_ColInfo.List)); } else { #if MANAGEDESENT_ON_WSA err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetColumnInfo( sesid.Value, dbid.Value, tablename, ignored, ref nativeColumnlist, nativeColumnlist.cbStruct, (uint)JET_ColInfo.List)); #endif } columnlist.SetFromNativeColumnlist(nativeColumnlist); return err; } /// /// Retrieves information about a column in a table. /// /// The session to use. /// The database that contains the table. /// The name of the table containing the column. /// The name of the column. /// Filled in with information about the columns in the table. /// An error if the call fails. public int JetGetColumnInfo( JET_SESID sesid, JET_DBID dbid, string tablename, string columnName, out JET_COLUMNBASE columnbase) { TraceFunctionCall(); CheckNotNull(tablename, "tablename"); CheckNotNull(columnName, "columnName"); int err; // Technically, this should have worked in Vista. But there was a bug, and // it was fixed after Windows 7. if (this.Capabilities.SupportsWindows8Features) { var nativeColumnbase = new NATIVE_COLUMNBASE_WIDE(); nativeColumnbase.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_COLUMNBASE_WIDE))); err = Err(NativeMethods.JetGetColumnInfoW( sesid.Value, dbid.Value, tablename, columnName, ref nativeColumnbase, nativeColumnbase.cbStruct, (uint)JET_ColInfo.Base)); columnbase = new JET_COLUMNBASE(nativeColumnbase); } else { var nativeColumnbase = new NATIVE_COLUMNBASE(); nativeColumnbase.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_COLUMNBASE))); #if MANAGEDESENT_ON_WSA err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetColumnInfo( sesid.Value, dbid.Value, tablename, columnName, ref nativeColumnbase, nativeColumnbase.cbStruct, (uint)JET_ColInfo.Base)); #endif columnbase = new JET_COLUMNBASE(nativeColumnbase); } return err; } /// /// Retrieves information about a column. /// /// The session to use. /// The database that contains the table. /// The name of the table containing the column. /// The columnid of the column. /// Filled in with information about the columns in the table. /// An error if the call fails. public int JetGetColumnInfo( JET_SESID sesid, JET_DBID dbid, string tablename, JET_COLUMNID columnid, out JET_COLUMNBASE columnbase) { TraceFunctionCall(); this.CheckSupportsVistaFeatures("JetGetColumnInfo"); CheckNotNull(tablename, "tablename"); int err; // Technically, this should have worked in Vista. But there was a bug, and // it was fixed after Windows 7. if (this.Capabilities.SupportsWindows8Features) { var nativeColumnbase = new NATIVE_COLUMNBASE_WIDE(); nativeColumnbase.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_COLUMNBASE_WIDE))); err = Err(NativeMethods.JetGetColumnInfoW( sesid.Value, dbid.Value, tablename, ref columnid.Value, ref nativeColumnbase, nativeColumnbase.cbStruct, (uint)VistaColInfo.BaseByColid)); columnbase = new JET_COLUMNBASE(nativeColumnbase); } else { var nativeColumnbase = new NATIVE_COLUMNBASE(); nativeColumnbase.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_COLUMNBASE))); #if MANAGEDESENT_ON_WSA err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetColumnInfo( sesid.Value, dbid.Value, tablename, ref columnid.Value, ref nativeColumnbase, nativeColumnbase.cbStruct, (uint)VistaColInfo.BaseByColid)); #endif columnbase = new JET_COLUMNBASE(nativeColumnbase); } return err; } #endregion #region JetGetObjectInfo overloads /// /// Retrieves information about database objects. /// /// The session to use. /// The database to use. /// Filled in with information about the objects in the database. /// An error if the call fails. public int JetGetObjectInfo(JET_SESID sesid, JET_DBID dbid, out JET_OBJECTLIST objectlist) { TraceFunctionCall(); objectlist = new JET_OBJECTLIST(); var nativeObjectlist = new NATIVE_OBJECTLIST(); nativeObjectlist.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_OBJECTLIST))); int err; if (this.Capabilities.SupportsVistaFeatures) { err = Err(NativeMethods.JetGetObjectInfoW( sesid.Value, dbid.Value, (uint)JET_objtyp.Table, null, null, ref nativeObjectlist, nativeObjectlist.cbStruct, (uint)JET_ObjInfo.ListNoStats)); } else { #if MANAGEDESENT_ON_WSA err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetObjectInfo( sesid.Value, dbid.Value, (uint)JET_objtyp.Table, null, null, ref nativeObjectlist, nativeObjectlist.cbStruct, (uint)JET_ObjInfo.ListNoStats)); #endif } objectlist.SetFromNativeObjectlist(nativeObjectlist); return err; } /// /// Retrieves information about database objects. /// /// The session to use. /// The database to use. /// The type of the object. /// The object name about which to retrieve information. /// Filled in with information about the objects in the database. /// An error if the call fails. public int JetGetObjectInfo( JET_SESID sesid, JET_DBID dbid, JET_objtyp objtyp, string objectName, out JET_OBJECTINFO objectinfo) { TraceFunctionCall(); objectinfo = new JET_OBJECTINFO(); var nativeObjectinfo = new NATIVE_OBJECTINFO(); nativeObjectinfo.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_OBJECTINFO))); int err; if (this.Capabilities.SupportsVistaFeatures) { err = Err(NativeMethods.JetGetObjectInfoW( sesid.Value, dbid.Value, (uint)objtyp, null, objectName, ref nativeObjectinfo, nativeObjectinfo.cbStruct, (uint)JET_ObjInfo.NoStats)); } else { #if MANAGEDESENT_ON_WSA err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetObjectInfo( sesid.Value, dbid.Value, (uint)objtyp, null, objectName, ref nativeObjectinfo, nativeObjectinfo.cbStruct, (uint)JET_ObjInfo.NoStats)); #endif } objectinfo.SetFromNativeObjectinfo(ref nativeObjectinfo); return err; } #endregion /// /// JetGetCurrentIndex function determines the name of the current /// index of a given cursor. This name is also used to later re-select /// that index as the current index using . It can /// also be used to discover the properties of that index using /// JetGetTableIndexInfo. /// /// The session to use. /// The cursor to get the index name for. /// Returns the name of the index. /// /// The maximum length of the index name. Index names are no more than /// Api.MaxNameLength characters. /// /// An error if the call fails. public int JetGetCurrentIndex(JET_SESID sesid, JET_TABLEID tableid, out string indexName, int maxNameLength) { TraceFunctionCall(); CheckNotNegative(maxNameLength, "maxNameLength"); var name = new StringBuilder(maxNameLength); #if MANAGEDESENT_ON_WSA int err = Err(NativeMethods.JetGetCurrentIndexW(sesid.Value, tableid.Value, name, checked((uint)maxNameLength))); #else int err = Err(NativeMethods.JetGetCurrentIndex(sesid.Value, tableid.Value, name, checked((uint)maxNameLength))); #endif indexName = name.ToString(); indexName = StringCache.TryToIntern(indexName); return err; } #region JetGetTableInfo overloads /// /// Retrieves various pieces of information about a table in a database. /// /// /// This overload is used with . /// /// The session to use. /// The table to retrieve information about. /// Retrieved information. /// The type of information to retrieve. /// An error if the call fails. public int JetGetTableInfo(JET_SESID sesid, JET_TABLEID tableid, out JET_OBJECTINFO result, JET_TblInfo infoLevel) { TraceFunctionCall(); var nativeResult = new NATIVE_OBJECTINFO(); int err; if (this.Capabilities.SupportsVistaFeatures) { err = Err( NativeMethods.JetGetTableInfoW( sesid.Value, tableid.Value, out nativeResult, checked((uint)Marshal.SizeOf(typeof(NATIVE_OBJECTINFO))), (uint)infoLevel)); } else { #if MANAGEDESENT_ON_WSA err = Err((int)JET_err.FeatureNotAvailable); #else err = Err( NativeMethods.JetGetTableInfo( sesid.Value, tableid.Value, out nativeResult, checked((uint)Marshal.SizeOf(typeof(NATIVE_OBJECTINFO))), (uint)infoLevel)); #endif } result = new JET_OBJECTINFO(); result.SetFromNativeObjectinfo(ref nativeResult); return err; } /// /// Retrieves various pieces of information about a table in a database. /// /// /// This overload is used with and /// . /// /// The session to use. /// The table to retrieve information about. /// Retrieved information. /// The type of information to retrieve. /// An error if the call fails. public int JetGetTableInfo(JET_SESID sesid, JET_TABLEID tableid, out string result, JET_TblInfo infoLevel) { TraceFunctionCall(); var resultBuffer = new StringBuilder(SystemParameters.NameMost + 1); int err; if (this.Capabilities.SupportsVistaFeatures) { err = Err(NativeMethods.JetGetTableInfoW( sesid.Value, tableid.Value, resultBuffer, (uint)resultBuffer.Capacity * sizeof(char), (uint)infoLevel)); } else { #if MANAGEDESENT_ON_WSA err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetTableInfo( sesid.Value, tableid.Value, resultBuffer, (uint)resultBuffer.Capacity, (uint)infoLevel)); #endif } result = resultBuffer.ToString(); result = StringCache.TryToIntern(result); return err; } /// /// Retrieves various pieces of information about a table in a database. /// /// /// This overload is used with . /// /// The session to use. /// The table to retrieve information about. /// Retrieved information. /// The type of information to retrieve. /// An error if the call fails. public int JetGetTableInfo(JET_SESID sesid, JET_TABLEID tableid, out JET_DBID result, JET_TblInfo infoLevel) { TraceFunctionCall(); result = JET_DBID.Nil; if (this.Capabilities.SupportsVistaFeatures) { return Err(NativeMethods.JetGetTableInfoW(sesid.Value, tableid.Value, out result.Value, sizeof(uint), (uint)infoLevel)); } else { #if MANAGEDESENT_ON_WSA return Err((int)JET_err.FeatureNotAvailable); #else return Err(NativeMethods.JetGetTableInfo(sesid.Value, tableid.Value, out result.Value, sizeof(uint), (uint)infoLevel)); #endif } } /// /// Retrieves various pieces of information about a table in a database. /// /// /// This overload is used with and /// . /// /// The session to use. /// The table to retrieve information about. /// Retrieved information. /// The type of information to retrieve. /// An error if the call fails. public int JetGetTableInfo(JET_SESID sesid, JET_TABLEID tableid, int[] result, JET_TblInfo infoLevel) { TraceFunctionCall(); CheckNotNull(result, "result"); uint bytesResult = checked((uint)(result.Length * sizeof(int))); if (this.Capabilities.SupportsVistaFeatures) { return Err(NativeMethods.JetGetTableInfoW(sesid.Value, tableid.Value, result, bytesResult, (uint)infoLevel)); } else { #if MANAGEDESENT_ON_WSA return Err((int)JET_err.FeatureNotAvailable); #else return Err(NativeMethods.JetGetTableInfo(sesid.Value, tableid.Value, result, bytesResult, (uint)infoLevel)); #endif } } /// /// Retrieves various pieces of information about a table in a database. /// /// /// This overload is used with and /// . /// /// The session to use. /// The table to retrieve information about. /// Retrieved information. /// The type of information to retrieve. /// An error if the call fails. public int JetGetTableInfo(JET_SESID sesid, JET_TABLEID tableid, out int result, JET_TblInfo infoLevel) { TraceFunctionCall(); uint nativeResult; int err; if (this.Capabilities.SupportsVistaFeatures) { err = Err(NativeMethods.JetGetTableInfoW(sesid.Value, tableid.Value, out nativeResult, sizeof(uint), (uint)infoLevel)); } else { #if MANAGEDESENT_ON_WSA nativeResult = 0; err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetTableInfo(sesid.Value, tableid.Value, out nativeResult, sizeof(uint), (uint)infoLevel)); #endif } result = unchecked((int)nativeResult); return 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 ushort result, JET_IdxInfo infoLevel) { TraceFunctionCall(); CheckNotNull(tablename, "tablename"); int err; if (this.Capabilities.SupportsVistaFeatures) { err = Err(NativeMethods.JetGetIndexInfoW( sesid.Value, dbid.Value, tablename, indexname, out result, sizeof(ushort), (uint)infoLevel)); } else { #if MANAGEDESENT_ON_WSA result = 0; err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetIndexInfo( sesid.Value, dbid.Value, tablename, indexname, out result, sizeof(ushort), (uint)infoLevel)); #endif } return err; } /// /// 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 int result, JET_IdxInfo infoLevel) { TraceFunctionCall(); CheckNotNull(tablename, "tablename"); uint nativeResult; int err; if (this.Capabilities.SupportsVistaFeatures) { err = Err(NativeMethods.JetGetIndexInfoW( sesid.Value, dbid.Value, tablename, indexname, out nativeResult, sizeof(uint), (uint)infoLevel)); } else { #if MANAGEDESENT_ON_WSA nativeResult = 0; err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetIndexInfo( sesid.Value, dbid.Value, tablename, indexname, out nativeResult, sizeof(uint), (uint)infoLevel)); #endif } result = unchecked((int)nativeResult); return err; } /// /// 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_INDEXID result, JET_IdxInfo infoLevel) { TraceFunctionCall(); CheckNotNull(tablename, "tablename"); int err; if (this.Capabilities.SupportsVistaFeatures) { err = Err(NativeMethods.JetGetIndexInfoW( sesid.Value, dbid.Value, tablename, indexname, out result, JET_INDEXID.SizeOfIndexId, (uint)infoLevel)); } else { #if MANAGEDESENT_ON_WSA result = new JET_INDEXID(); err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetIndexInfo( sesid.Value, dbid.Value, tablename, indexname, out result, JET_INDEXID.SizeOfIndexId, (uint)infoLevel)); #endif } return err; } /// /// 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_INDEXLIST result, JET_IdxInfo infoLevel) { TraceFunctionCall(); CheckNotNull(tablename, "tablename"); int err; var nativeIndexlist = new NATIVE_INDEXLIST(); nativeIndexlist.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_INDEXLIST))); if (this.Capabilities.SupportsVistaFeatures) { err = Err(NativeMethods.JetGetIndexInfoW( sesid.Value, dbid.Value, tablename, indexname, ref nativeIndexlist, nativeIndexlist.cbStruct, (uint)infoLevel)); } else { #if MANAGEDESENT_ON_WSA err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetIndexInfo( sesid.Value, dbid.Value, tablename, indexname, ref nativeIndexlist, nativeIndexlist.cbStruct, (uint)infoLevel)); #endif } result = new JET_INDEXLIST(); result.SetFromNativeIndexlist(nativeIndexlist); return err; } /// /// 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 string result, JET_IdxInfo infoLevel) { TraceFunctionCall(); CheckNotNull(tablename, "tablename"); int err; // Will need to check for Windows 8 Features. // // Currently only JET_IdxInfo.LocaleName is supported. uint bytesMax = checked((uint)SystemParameters.LocaleNameMaxLength * sizeof(char)); var stringBuilder = new StringBuilder(SystemParameters.LocaleNameMaxLength); err = Err(NativeMethods.JetGetIndexInfoW( sesid.Value, dbid.Value, tablename, indexname, stringBuilder, bytesMax, (uint)infoLevel)); result = stringBuilder.ToString(); result = StringCache.TryToIntern(result); return err; } #endregion #region JetGetTableIndexInfo overloads /// /// Retrieves information about indexes on a table. /// /// The session to use. /// The table to retrieve index information about. /// The name of the index. /// 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 ushort result, JET_IdxInfo infoLevel) { TraceFunctionCall(); int err; if (this.Capabilities.SupportsVistaFeatures) { err = Err(NativeMethods.JetGetTableIndexInfoW( sesid.Value, tableid.Value, indexname, out result, sizeof(ushort), (uint)infoLevel)); } else { #if MANAGEDESENT_ON_WSA result = 0; err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetTableIndexInfo( sesid.Value, tableid.Value, indexname, out result, sizeof(ushort), (uint)infoLevel)); #endif } 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. /// 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 int result, JET_IdxInfo infoLevel) { TraceFunctionCall(); uint nativeResult; int err; if (this.Capabilities.SupportsVistaFeatures) { err = Err(NativeMethods.JetGetTableIndexInfoW( sesid.Value, tableid.Value, indexname, out nativeResult, sizeof(uint), (uint)infoLevel)); } else { #if MANAGEDESENT_ON_WSA nativeResult = 0; err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetTableIndexInfo( sesid.Value, tableid.Value, indexname, out nativeResult, sizeof(uint), (uint)infoLevel)); #endif } result = unchecked((int)nativeResult); 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. /// 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_INDEXID result, JET_IdxInfo infoLevel) { TraceFunctionCall(); int err; if (this.Capabilities.SupportsVistaFeatures) { err = Err(NativeMethods.JetGetTableIndexInfoW( sesid.Value, tableid.Value, indexname, out result, JET_INDEXID.SizeOfIndexId, (uint)infoLevel)); } else { #if MANAGEDESENT_ON_WSA result = new JET_INDEXID(); err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetTableIndexInfo( sesid.Value, tableid.Value, indexname, out result, JET_INDEXID.SizeOfIndexId, (uint)infoLevel)); #endif } 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. /// 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_INDEXLIST result, JET_IdxInfo infoLevel) { TraceFunctionCall(); var nativeIndexlist = new NATIVE_INDEXLIST(); nativeIndexlist.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_INDEXLIST))); int err; if (this.Capabilities.SupportsVistaFeatures) { err = Err(NativeMethods.JetGetTableIndexInfoW( sesid.Value, tableid.Value, indexname, ref nativeIndexlist, nativeIndexlist.cbStruct, (uint)infoLevel)); } else { #if MANAGEDESENT_ON_WSA err = Err((int)JET_err.FeatureNotAvailable); #else err = Err(NativeMethods.JetGetTableIndexInfo( sesid.Value, tableid.Value, indexname, ref nativeIndexlist, nativeIndexlist.cbStruct, (uint)infoLevel)); #endif } result = new JET_INDEXLIST(); result.SetFromNativeIndexlist(nativeIndexlist); 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. /// 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 string result, JET_IdxInfo infoLevel) { TraceFunctionCall(); // Will need to check for Windows 8 Features. // // Currently only JET_IdxInfo.LocaleName is supported. uint bytesMax = checked((uint)SystemParameters.LocaleNameMaxLength * sizeof(char)); var stringBuilder = new StringBuilder(SystemParameters.LocaleNameMaxLength); int err; err = Err(NativeMethods.JetGetTableIndexInfoW( sesid.Value, tableid.Value, indexname, stringBuilder, bytesMax, (uint)infoLevel)); result = stringBuilder.ToString(); result = StringCache.TryToIntern(result); return err; } #endregion /// /// Changes the name of an existing table. /// /// The session to use. /// The database containing the table. /// The name of the table. /// The new name of the table. /// An error if the call fails. public int JetRenameTable(JET_SESID sesid, JET_DBID dbid, string tableName, string newTableName) { TraceFunctionCall(); CheckNotNull(tableName, "tableName"); CheckNotNull(newTableName, "newTableName"); #if MANAGEDESENT_ON_WSA return Err(NativeMethods.JetRenameTableW(sesid.Value, dbid.Value, tableName, newTableName)); #else return Err(NativeMethods.JetRenameTable(sesid.Value, dbid.Value, tableName, newTableName)); #endif } /// /// Changes the name of an existing column. /// /// The session to use. /// The table containing the column. /// The name of the column. /// The new name of the column. /// Column rename options. /// An error if the call fails. public int JetRenameColumn(JET_SESID sesid, JET_TABLEID tableid, string name, string newName, RenameColumnGrbit grbit) { TraceFunctionCall(); CheckNotNull(name, "name"); CheckNotNull(newName, "newName"); #if MANAGEDESENT_ON_WSA return Err( NativeMethods.JetRenameColumnW(sesid.Value, tableid.Value, name, newName, (uint)grbit)); #else return Err( NativeMethods.JetRenameColumn(sesid.Value, tableid.Value, name, newName, (uint)grbit)); #endif } #if !MANAGEDESENT_ON_WSA // Not exposed in MSDK /// /// Changes the default value of an existing column. /// /// The session to use. /// The database containing the column. /// The name of the table containing the column. /// The name of the column. /// The new default value. /// Size of the new default value. /// Column default value options. /// An error if the call fails. public int JetSetColumnDefaultValue( JET_SESID sesid, JET_DBID dbid, string tableName, string columnName, byte[] data, int dataSize, SetColumnDefaultValueGrbit grbit) { TraceFunctionCall(); CheckNotNull(tableName, "tableName"); CheckNotNull(columnName, "columnName"); CheckDataSize(data, dataSize, "dataSize"); return Err( NativeMethods.JetSetColumnDefaultValue( sesid.Value, dbid.Value, tableName, columnName, data, checked((uint)dataSize), (uint)grbit)); } #endif // !MANAGEDESENT_ON_WSA #endregion #region Navigation /// /// Positions a cursor to an index entry for the record that is associated with /// the specified bookmark. The bookmark can be used with any index defined over /// a table. The bookmark for a record can be retrieved using . /// /// The session to use. /// The cursor to position. /// The bookmark used to position the cursor. /// The size of the bookmark. /// An error if the call fails. public int JetGotoBookmark(JET_SESID sesid, JET_TABLEID tableid, byte[] bookmark, int bookmarkSize) { TraceFunctionCall(); CheckNotNull(bookmark, "bookmark"); CheckDataSize(bookmark, bookmarkSize, "bookmarkSize"); return Err( NativeMethods.JetGotoBookmark( sesid.Value, tableid.Value, bookmark, checked((uint)bookmarkSize))); } /// /// Positions a cursor to an index entry that is associated with the /// specified secondary index bookmark. The secondary index bookmark /// must be used with the same index over the same table from which it /// was originally retrieved. The secondary index bookmark for an index /// entry can be retrieved using . /// /// The session to use. /// The table cursor to position. /// The buffer that contains the secondary key. /// The size of the secondary key. /// The buffer that contains the primary key. /// The size of the primary key. /// Options for positioning the bookmark. /// An error if the call fails. public int JetGotoSecondaryIndexBookmark( JET_SESID sesid, JET_TABLEID tableid, byte[] secondaryKey, int secondaryKeySize, byte[] primaryKey, int primaryKeySize, GotoSecondaryIndexBookmarkGrbit grbit) { TraceFunctionCall(); CheckNotNull(secondaryKey, "secondaryKey"); CheckDataSize(secondaryKey, secondaryKeySize, "secondaryKeySize"); CheckDataSize(primaryKey, primaryKeySize, "primaryKeySize"); return Err( NativeMethods.JetGotoSecondaryIndexBookmark( sesid.Value, tableid.Value, secondaryKey, checked((uint)secondaryKeySize), primaryKey, checked((uint)primaryKeySize), (uint)grbit)); } /// /// Constructs search keys that may then be used by and . /// /// /// The MakeKey functions provide datatype-specific make key functionality. /// /// The session to use. /// The cursor to create the key on. /// Column data for the current key column of the current index. /// Size of the data. /// Key options. /// An error if the call fails. public int JetMakeKey(JET_SESID sesid, JET_TABLEID tableid, IntPtr data, int dataSize, MakeKeyGrbit grbit) { TraceFunctionCall(); CheckNotNegative(dataSize, "dataSize"); return Err(NativeMethods.JetMakeKey(sesid.Value, tableid.Value, data, checked((uint)dataSize), unchecked((uint)grbit))); } /// /// Efficiently positions a cursor to an index entry that matches the search /// criteria specified by the search key in that cursor and the specified /// inequality. A search key must have been previously constructed using /// JetMakeKey. /// /// The session to use. /// The cursor to position. /// Seek options. /// An error or warning.. public int JetSeek(JET_SESID sesid, JET_TABLEID tableid, SeekGrbit grbit) { TraceFunctionCall(); return Err(NativeMethods.JetSeek(sesid.Value, tableid.Value, unchecked((uint)grbit))); } /// /// Navigate through an index. The cursor can be positioned at the start or /// end of the index and moved backwards and forwards by a specified number /// of index entries. /// /// The session to use for the call. /// The cursor to position. /// An offset which indicates how far to move the cursor. /// Move options. /// An error if the call fails. public int JetMove(JET_SESID sesid, JET_TABLEID tableid, int numRows, MoveGrbit grbit) { TraceFunctionCall(); return Err(NativeMethods.JetMove(sesid.Value, tableid.Value, numRows, unchecked((uint)grbit))); } /// /// Temporarily limits the set of index entries that the cursor can walk using /// to those starting /// from the current index entry and ending at the index entry that matches the /// search criteria specified by the search key in that cursor and the specified /// bound criteria. A search key must have been previously constructed using /// JetMakeKey. /// /// The session to use. /// The cursor to set the index range on. /// Index range options. /// An error if the call fails. public int JetSetIndexRange(JET_SESID sesid, JET_TABLEID tableid, SetIndexRangeGrbit grbit) { TraceFunctionCall(); return Err(NativeMethods.JetSetIndexRange(sesid.Value, tableid.Value, unchecked((uint)grbit))); } /// /// Computes the intersection between multiple sets of index entries from different secondary /// indices over the same table. This operation is useful for finding the set of records in a /// table that match two or more criteria that can be expressed using index ranges. /// /// The session to use. /// /// An the index ranges to intersect. The tableids in the ranges /// must have index ranges set on them. /// /// /// The number of index ranges. /// /// /// Returns information about the temporary table containing the intersection results. /// /// Intersection options. /// An error if the call fails. public int JetIntersectIndexes( JET_SESID sesid, JET_INDEXRANGE[] ranges, int numRanges, out JET_RECORDLIST recordlist, IntersectIndexesGrbit grbit) { TraceFunctionCall(); CheckNotNull(ranges, "ranges"); CheckDataSize(ranges, numRanges, "numRanges"); if (numRanges < 2) { throw new ArgumentOutOfRangeException( "numRanges", numRanges, "JetIntersectIndexes requires at least two index ranges."); } var indexRanges = new NATIVE_INDEXRANGE[numRanges]; for (int i = 0; i < numRanges; ++i) { indexRanges[i] = ranges[i].GetNativeIndexRange(); } var nativeRecordlist = new NATIVE_RECORDLIST(); nativeRecordlist.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_RECORDLIST))); int err = Err( NativeMethods.JetIntersectIndexes( sesid.Value, indexRanges, checked((uint)indexRanges.Length), ref nativeRecordlist, (uint)grbit)); recordlist = new JET_RECORDLIST(); recordlist.SetFromNativeRecordlist(nativeRecordlist); return err; } /// /// Set the current index of a cursor. /// /// The session to use. /// The cursor to set the index on. /// /// The name of the index to be selected. If this is null or empty the primary /// index will be selected. /// /// An error if the call fails. public int JetSetCurrentIndex(JET_SESID sesid, JET_TABLEID tableid, string index) { #if MANAGEDESENT_ON_WSA return this.JetSetCurrentIndex4(sesid, tableid, index, IntPtr.Zero, SetCurrentIndexGrbit.MoveFirst, 1); #else TraceFunctionCall(); // A null index name is valid here -- it will set the table to the primary index return Err(NativeMethods.JetSetCurrentIndex(sesid.Value, tableid.Value, index)); #endif } /// /// Set the current index of a cursor. /// /// The session to use. /// The cursor to set the index on. /// /// The name of the index to be selected. If this is null or empty the primary /// index will be selected. /// /// /// Set index options. /// /// An error if the call fails. public int JetSetCurrentIndex2(JET_SESID sesid, JET_TABLEID tableid, string index, SetCurrentIndexGrbit grbit) { #if MANAGEDESENT_ON_WSA return this.JetSetCurrentIndex4(sesid, tableid, index, IntPtr.Zero, grbit, 1); #else TraceFunctionCall(); // A null index name is valid here -- it will set the table to the primary index return Err(NativeMethods.JetSetCurrentIndex2(sesid.Value, tableid.Value, index, (uint)grbit)); #endif } /// /// Set the current index of a cursor. /// /// The session to use. /// The cursor to set the index on. /// /// The name of the index to be selected. If this is null or empty the primary /// index will be selected. /// /// /// Set index options. /// /// /// Sequence number of the multi-valued column value which will be used /// to position the cursor on the new index. This parameter is only used /// in conjunction with . When /// this parameter is not present or is set to zero, its value is presumed /// to be 1. /// /// An error if the call fails. public int JetSetCurrentIndex3(JET_SESID sesid, JET_TABLEID tableid, string index, SetCurrentIndexGrbit grbit, int itagSequence) { #if MANAGEDESENT_ON_WSA return this.JetSetCurrentIndex4(sesid, tableid, index, IntPtr.Zero, grbit, itagSequence); #else TraceFunctionCall(); // A null index name is valid here -- it will set the table to the primary index return Err(NativeMethods.JetSetCurrentIndex3(sesid.Value, tableid.Value, index, (uint)grbit, checked((uint)itagSequence))); #endif } /// /// Set the current index of a cursor. /// /// The session to use. /// The cursor to set the index on. /// /// The name of the index to be selected. If this is null or empty the primary /// index will be selected. /// /// /// The id of the index to select. This id can be obtained using JetGetIndexInfo /// or JetGetTableIndexInfo with the option. /// /// /// Set index options. /// /// /// Sequence number of the multi-valued column value which will be used /// to position the cursor on the new index. This parameter is only used /// in conjunction with . When /// this parameter is not present or is set to zero, its value is presumed /// to be 1. /// /// An error if the call fails. public int JetSetCurrentIndex4( JET_SESID sesid, JET_TABLEID tableid, string index, JET_INDEXID indexid, SetCurrentIndexGrbit grbit, int itagSequence) { TraceFunctionCall(); // A null index name is valid here -- it will set the table to the primary index #if MANAGEDESENT_ON_WSA return Err(NativeMethods.JetSetCurrentIndex4W(sesid.Value, tableid.Value, index, ref indexid, (uint)grbit, checked((uint)itagSequence))); #else return Err(NativeMethods.JetSetCurrentIndex4(sesid.Value, tableid.Value, index, ref indexid, (uint)grbit, checked((uint)itagSequence))); #endif } /// /// Counts the number of entries in the current index from the current position forward. /// The current position is included in the count. The count can be greater than the /// total number of records in the table if the current index is over a multi-valued /// column and instances of the column have multiple-values. If the table is empty, /// then 0 will be returned for the count. /// /// The session to use. /// The cursor to count the records in. /// Returns the number of records. /// /// The maximum number of records to count. /// /// An error if the call fails. public int JetIndexRecordCount(JET_SESID sesid, JET_TABLEID tableid, out int numRecords, int maxRecordsToCount) { TraceFunctionCall(); CheckNotNegative(maxRecordsToCount, "maxRecordsToCount"); uint crec = 0; int err = Err(NativeMethods.JetIndexRecordCount(sesid.Value, tableid.Value, out crec, unchecked((uint)maxRecordsToCount))); // -1 is allowed numRecords = checked((int)crec); return err; } /// /// Counts the number of entries in the current index from the current position forward. /// The current position is included in the count. The count can be greater than the /// total number of records in the table if the current index is over a multi-valued /// column and instances of the column have multiple-values. If the table is empty, /// then 0 will be returned for the count. /// /// The session to use. /// The cursor to count the records in. /// Returns the number of records. /// /// The maximum number of records to count. /// /// An error if the call fails. public int JetIndexRecordCount2(JET_SESID sesid, JET_TABLEID tableid, out long numRecords, long maxRecordsToCount) { TraceFunctionCall(); CheckNotNegative(maxRecordsToCount, "maxRecordsToCount"); ulong crec = 0; int err = Err(NativeMethods.JetIndexRecordCount2(sesid.Value, tableid.Value, out crec, unchecked((ulong)maxRecordsToCount))); numRecords = checked((long)crec); return err; } /// /// Notifies the database engine that the application is scanning the entire /// index that the cursor is positioned on. Consequently, the methods that /// are used to access the index data will be tuned to make this scenario as /// fast as possible. /// /// The session to use. /// The cursor that will be accessing the data. /// Reserved for future use. /// An error if the call fails. public int JetSetTableSequential(JET_SESID sesid, JET_TABLEID tableid, SetTableSequentialGrbit grbit) { TraceFunctionCall(); return Err(NativeMethods.JetSetTableSequential(sesid.Value, tableid.Value, (uint)grbit)); } /// /// Notifies the database engine that the application is no longer scanning the /// entire index the cursor is positioned on. This call reverses a notification /// sent by JetSetTableSequential. /// /// The session to use. /// The cursor that was accessing the data. /// Reserved for future use. /// An error if the call fails. public int JetResetTableSequential(JET_SESID sesid, JET_TABLEID tableid, ResetTableSequentialGrbit grbit) { TraceFunctionCall(); return Err(NativeMethods.JetResetTableSequential(sesid.Value, tableid.Value, (uint)grbit)); } /// /// Returns the fractional position of the current record in the current index /// in the form of a JET_RECPOS structure. /// /// The session to use. /// The cursor positioned on the record. /// Returns the approximate fractional position of the record. /// An error if the call fails. public int JetGetRecordPosition(JET_SESID sesid, JET_TABLEID tableid, out JET_RECPOS recpos) { TraceFunctionCall(); recpos = new JET_RECPOS(); NATIVE_RECPOS native = recpos.GetNativeRecpos(); int err = Err(NativeMethods.JetGetRecordPosition(sesid.Value, tableid.Value, out native, native.cbStruct)); recpos.SetFromNativeRecpos(native); return err; } /// /// Moves a cursor to a new location that is a fraction of the way through /// the current index. /// /// The session to use. /// The cursor to position. /// The approximate position to move to. /// An error if the call fails. public int JetGotoPosition(JET_SESID sesid, JET_TABLEID tableid, JET_RECPOS recpos) { TraceFunctionCall(); NATIVE_RECPOS native = recpos.GetNativeRecpos(); return Err(NativeMethods.JetGotoPosition(sesid.Value, tableid.Value, ref native)); } /// /// If the records with the specified keys 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 keys to preread. The keys must be sorted. /// /// The lengths of the keys to preread. /// /// The index of the first key in the keys array to read. /// /// /// The maximum number of keys to preread. /// /// /// Returns the number of keys to actually preread. /// /// /// Preread options. Used to specify the direction of the preread. /// /// An error or warning. public int JetPrereadKeys( JET_SESID sesid, JET_TABLEID tableid, byte[][] keys, int[] keyLengths, int keyIndex, int keyCount, out int keysPreread, PrereadKeysGrbit grbit) { TraceFunctionCall(); this.CheckSupportsWindows7Features("JetPrereadKeys"); CheckDataSize(keys, keyIndex, "keyIndex", keyCount, "keyCount"); CheckDataSize(keyLengths, keyIndex, "keyIndex", keyCount, "keyCount"); CheckNotNull(keys, "keys"); CheckNotNull(keyLengths, "keyLengths"); int err; unsafe { void** rgpvKeys = stackalloc void*[keyCount]; // [7/21/2010] StyleCop error? You need at least the 4.4 release of StyleCop uint* rgcbKeys = stackalloc uint[keyCount]; using (var gchandlecollection = new GCHandleCollection()) { gchandlecollection.SetCapacity(keyCount); for (int i = 0; i < keyCount; ++i) { rgpvKeys[i] = (void*)gchandlecollection.Add(keys[keyIndex + i]); rgcbKeys[i] = checked((uint)keyLengths[keyIndex + i]); } err = Err(NativeMethods.JetPrereadKeys( sesid.Value, tableid.Value, rgpvKeys, rgcbKeys, keyCount, out keysPreread, (uint)grbit)); } } return err; } #endregion #region Data Retrieval /// /// 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 . /// The bookmark will be no longer than /// bytes. /// /// The session to use. /// The cursor to retrieve the bookmark from. /// Buffer to contain the bookmark. /// Size of the bookmark buffer. /// Returns the actual size of the bookmark. /// An error if the call fails. public int JetGetBookmark(JET_SESID sesid, JET_TABLEID tableid, byte[] bookmark, int bookmarkSize, out int actualBookmarkSize) { TraceFunctionCall(); CheckDataSize(bookmark, bookmarkSize, "bookmarkSize"); uint bytesActual = 0; int err = Err( NativeMethods.JetGetBookmark( sesid.Value, tableid.Value, bookmark, checked((uint)bookmarkSize), out bytesActual)); actualBookmarkSize = GetActualSize(bytesActual); return err; } /// /// Retrieves a special bookmark for the secondary index entry at the /// current position of a cursor. This bookmark can then be used to /// efficiently reposition that cursor back to the same index entry /// using JetGotoSecondaryIndexBookmark. This is most useful when /// repositioning on a secondary index that contains duplicate keys or /// that contains multiple index entries for the same record. /// /// The session to use. /// The cursor to retrieve the bookmark from. /// Output buffer for the secondary key. /// Size of the secondary key buffer. /// Returns the size of the secondary key. /// Output buffer for the primary key. /// Size of the primary key buffer. /// Returns the size of the primary key. /// Options for the call. /// An error if the call fails. public int JetGetSecondaryIndexBookmark( JET_SESID sesid, JET_TABLEID tableid, byte[] secondaryKey, int secondaryKeySize, out int actualSecondaryKeySize, byte[] primaryKey, int primaryKeySize, out int actualPrimaryKeySize, GetSecondaryIndexBookmarkGrbit grbit) { TraceFunctionCall(); CheckDataSize(secondaryKey, secondaryKeySize, "secondaryKeySize"); CheckDataSize(primaryKey, primaryKeySize, "primaryKeySize"); uint bytesSecondaryKey = 0; uint bytesPrimaryKey = 0; int err = Err( NativeMethods.JetGetSecondaryIndexBookmark( sesid.Value, tableid.Value, secondaryKey, checked((uint)secondaryKeySize), out bytesSecondaryKey, primaryKey, checked((uint)primaryKeySize), out bytesPrimaryKey, (uint)grbit)); actualSecondaryKeySize = GetActualSize(bytesSecondaryKey); actualPrimaryKeySize = GetActualSize(bytesPrimaryKey); return err; } /// /// 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. /// The buffer to retrieve the key into. /// The size of the buffer. /// Returns the actual size of the data. /// Retrieve key options. /// An error if the call fails. public int JetRetrieveKey(JET_SESID sesid, JET_TABLEID tableid, byte[] data, int dataSize, out int actualDataSize, RetrieveKeyGrbit grbit) { TraceFunctionCall(); CheckDataSize(data, dataSize, "dataSize"); uint bytesActual = 0; int err = Err(NativeMethods.JetRetrieveKey(sesid.Value, tableid.Value, data, checked((uint)dataSize), out bytesActual, unchecked((uint)grbit))); actualDataSize = GetActualSize(bytesActual); return err; } /// /// 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 RetrieveColumnAs functions provide datatype-specific retrieval functions. /// /// The session to use. /// The cursor to retrieve the column from. /// The columnid to retrieve. /// The data buffer to be retrieved into. /// The size of the data buffer. /// Returns the actual size of the data buffer. /// 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). /// /// An error or warning. public int JetRetrieveColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, IntPtr data, int dataSize, out int actualDataSize, RetrieveColumnGrbit grbit, JET_RETINFO retinfo) { TraceFunctionCall(); CheckNotNegative(dataSize, "dataSize"); int err; uint bytesActual = 0; if (null != retinfo) { NATIVE_RETINFO nativeRetinfo = retinfo.GetNativeRetinfo(); err = Err(NativeMethods.JetRetrieveColumn( sesid.Value, tableid.Value, columnid.Value, data, checked((uint)dataSize), out bytesActual, unchecked((uint)grbit), ref nativeRetinfo)); retinfo.SetFromNativeRetinfo(nativeRetinfo); } else { err = Err(NativeMethods.JetRetrieveColumn( sesid.Value, tableid.Value, columnid.Value, data, checked((uint)dataSize), out bytesActual, unchecked((uint)grbit), IntPtr.Zero)); } actualDataSize = checked((int)bytesActual); return err; } /// /// The JetRetrieveColumns function retrieves multiple column values /// from the current record in a single operation. An array of /// structures is used to /// describe the set of column values to be retrieved, and to describe /// output buffers for each column value to be retrieved. /// /// The session to use. /// The cursor to retrieve columns from. /// /// An array of one or more JET_RETRIEVECOLUMN structures. Each /// structure includes descriptions of which column value to retrieve /// and where to store returned data. /// /// /// Number of structures in the array given by retrievecolumns. /// /// /// An error or warning. /// public unsafe int JetRetrieveColumns(JET_SESID sesid, JET_TABLEID tableid, NATIVE_RETRIEVECOLUMN* retrievecolumns, int numColumns) { TraceFunctionCall(); return Err(NativeMethods.JetRetrieveColumns(sesid.Value, tableid.Value, retrievecolumns, checked((uint)numColumns))); } /// /// Efficiently retrieves a set of columns and their values from the /// current record of a cursor or the copy buffer of that cursor. The /// columns and values retrieved can be restricted by a list of /// column IDs, itagSequence numbers, and other characteristics. This /// column retrieval API is unique in that it returns information in /// dynamically allocated memory that is obtained using a /// user-provided realloc compatible callback. This new flexibility /// permits the efficient retrieval of column data with specific /// characteristics (such as size and multiplicity) that are unknown /// to the caller. This eliminates the need for the use of the discovery /// modes of JetRetrieveColumn to determine those /// characteristics in order to setup a final call to /// JetRetrieveColumn that will successfully retrieve /// the desired data. /// /// The session to use. /// The cursor to retrieve data from. /// The numbers of JET_ENUMCOLUMNIDS. /// /// An optional array of column IDs, each with an optional array of itagSequence /// numbers to enumerate. /// /// /// Returns the number of column values retrieved. /// /// /// Returns the enumerated column values. /// /// /// Callback used to allocate memory. /// /// /// Context for the allocation callback. /// /// /// Sets a cap on the amount of data to return from a long text or long /// binary column. This parameter can be used to prevent the enumeration /// of an extremely large column value. /// /// Retrieve options. /// A warning, error or success. public int JetEnumerateColumns( JET_SESID sesid, JET_TABLEID tableid, int numColumnids, JET_ENUMCOLUMNID[] columnids, out int numColumnValues, out JET_ENUMCOLUMN[] columnValues, JET_PFNREALLOC allocator, IntPtr allocatorContext, int maxDataSize, EnumerateColumnsGrbit grbit) { TraceFunctionCall(); CheckNotNull(allocator, "allocator"); CheckNotNegative(maxDataSize, "maxDataSize"); CheckDataSize(columnids, numColumnids, "numColumnids"); unsafe { // Converting to the native structs is a bit complex because we // do not want to allocate heap memory for this operations. We // allocate the NATIVE_ENUMCOLUMNID array on the stack and // convert the managed objects. During the conversion pass we // calculate the total size of the tags. An array for the tags // is then allocated and a second pass converts the tags. // // Because we are using stackalloc all the work has to be done // in the same method. NATIVE_ENUMCOLUMNID* nativecolumnids = stackalloc NATIVE_ENUMCOLUMNID[numColumnids]; int totalNumTags = ConvertEnumColumnids(columnids, numColumnids, nativecolumnids); uint* tags = stackalloc uint[totalNumTags]; ConvertEnumColumnidTags(columnids, numColumnids, nativecolumnids, tags); uint numEnumColumn; NATIVE_ENUMCOLUMN* nativeenumcolumns; int err = NativeMethods.JetEnumerateColumns( sesid.Value, tableid.Value, checked((uint)numColumnids), numColumnids > 0 ? nativecolumnids : null, out numEnumColumn, out nativeenumcolumns, allocator, allocatorContext, checked((uint)maxDataSize), (uint)grbit); ConvertEnumerateColumnsResult(allocator, allocatorContext, numEnumColumn, nativeenumcolumns, out numColumnValues, out columnValues); return Err(err); } } /// /// 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. /// A warning or success. public int JetEnumerateColumns( JET_SESID sesid, JET_TABLEID tableid, EnumerateColumnsGrbit grbit, out IEnumerable enumeratedColumns) { unsafe { // NOTE: We must never throw an exception up through ESE or it will corrupt its internal state! Exception allocatorException = null; JET_PFNREALLOC allocator = (c, pv, cb) => { try { if (pv == IntPtr.Zero) { // NOTE: this will allocate memory if cb == 0 and that is what we want. return Marshal.AllocHGlobal(new IntPtr(cb)); } if (cb == 0) { Marshal.FreeHGlobal(pv); return IntPtr.Zero; } return Marshal.ReAllocHGlobal(pv, new IntPtr(cb)); } catch (OutOfMemoryException) { return IntPtr.Zero; } #if !MANAGEDESENT_ON_WSA // Thread model has changed in windows store apps. catch (ThreadAbortException e) { LibraryHelpers.ThreadResetAbort(); allocatorException = e; return IntPtr.Zero; } #endif catch (Exception e) { allocatorException = e; return IntPtr.Zero; } }; uint nativeEnumColumnCount; NATIVE_ENUMCOLUMN* nativeEnumColumns; int err = Implementation.NativeMethods.JetEnumerateColumns( sesid.Value, tableid.Value, 0, null, out nativeEnumColumnCount, out nativeEnumColumns, allocator, IntPtr.Zero, int.MaxValue, (uint)(grbit & ~EnumerateColumnsGrbit.EnumerateCompressOutput)); var columns = new EnumeratedColumn[nativeEnumColumnCount]; for (int i = 0; i < nativeEnumColumnCount; ++i) { columns[i] = new EnumeratedColumn(); columns[i].Id = new JET_COLUMNID { Value = nativeEnumColumns[i].columnid }; columns[i].Error = nativeEnumColumns[i].err < 0 ? (JET_err)nativeEnumColumns[i].err : JET_err.Success; columns[i].Warning = nativeEnumColumns[i].err > 0 ? (JET_wrn)nativeEnumColumns[i].err : JET_wrn.Success; if ((JET_wrn)nativeEnumColumns[i].err == JET_wrn.Success) { EnumeratedColumn.Value[] values = new EnumeratedColumn.Value[nativeEnumColumns[i].cEnumColumnValue]; columns[i].Values = values; for (int j = 0; j < nativeEnumColumns[i].cEnumColumnValue; j++) { values[j] = new EnumeratedColumn.Value(); values[j].Ordinal = j + 1; values[j].Warning = (JET_wrn)nativeEnumColumns[i].rgEnumColumnValue[j].err; values[j].Bytes = new byte[(int)nativeEnumColumns[i].rgEnumColumnValue[j].cbData]; Marshal.Copy( nativeEnumColumns[i].rgEnumColumnValue[j].pvData, values[j].Bytes, 0, (int)nativeEnumColumns[i].rgEnumColumnValue[j].cbData); if (nativeEnumColumns[i].rgEnumColumnValue[j].pvData != IntPtr.Zero) { allocator(IntPtr.Zero, nativeEnumColumns[i].rgEnumColumnValue[j].pvData, 0); } } if (nativeEnumColumns[i].rgEnumColumnValue != null) { allocator(IntPtr.Zero, new IntPtr(nativeEnumColumns[i].rgEnumColumnValue), 0); } } } if (nativeEnumColumns != null) { allocator(IntPtr.Zero, new IntPtr(nativeEnumColumns), 0); } if (allocatorException != null) { #if !MANAGEDESENT_ON_WSA // Thread model has changed in Windows store apps. if (allocatorException is ThreadAbortException) { Thread.CurrentThread.Abort(); } #endif throw allocatorException; } enumeratedColumns = columns; return Err(err); } } #if !MANAGEDESENT_ON_WSA // Not exposed in MSDK /// /// Retrieves record size information from the desired location. /// /// The session to use. /// /// The cursor that will be used for the API call. The cursor must be /// positioned on a record, or have an update prepared. /// /// Returns the size of the record. /// Call options. /// A warning, error or success. public int JetGetRecordSize(JET_SESID sesid, JET_TABLEID tableid, ref JET_RECSIZE recsize, GetRecordSizeGrbit grbit) { TraceFunctionCall(); this.CheckSupportsVistaFeatures("JetGetRecordSize"); int err; // Use JetGetRecordSize2 if available, otherwise JetGetRecordSize. if (this.Capabilities.SupportsWindows7Features) { var native = recsize.GetNativeRecsize2(); err = NativeMethods.JetGetRecordSize2(sesid.Value, tableid.Value, ref native, (uint)grbit); recsize.SetFromNativeRecsize(native); } else { var native = recsize.GetNativeRecsize(); err = NativeMethods.JetGetRecordSize(sesid.Value, tableid.Value, ref native, (uint)grbit); recsize.SetFromNativeRecsize(native); } return Err(err); } #endif // !MANAGEDESENT_ON_WSA #endregion #region DML /// /// Deletes the current record in a database table. /// /// The session that opened the cursor. /// The cursor on a database table. The current row will be deleted. /// An error if the call fails. public int JetDelete(JET_SESID sesid, JET_TABLEID tableid) { TraceFunctionCall(); return Err(NativeMethods.JetDelete(sesid.Value, tableid.Value)); } /// /// Prepare a cursor for update. /// /// The session which is starting the update. /// The cursor to start the update for. /// The type of update to prepare. /// An error if the call fails. public int JetPrepareUpdate(JET_SESID sesid, JET_TABLEID tableid, JET_prep prep) { TraceFunctionCall(); return Err(NativeMethods.JetPrepareUpdate(sesid.Value, tableid.Value, unchecked((uint)prep))); } /// /// The JetUpdate function performs an update operation including inserting a new row into /// a table or updating an existing row. Deleting a table row is performed by calling /// . /// /// The session which started the update. /// The cursor to update. An update should be prepared. /// Returns the bookmark of the updated record. This can be null. /// The size of the bookmark buffer. /// Returns the actual size of the bookmark. /// /// JetUpdate is the final step in performing an insert or an update. The update is begun by /// calling and then by calling /// JetSetColumn /// one or more times to set the record state. Finally, JetUpdate /// is called to complete the update operation. Indexes are updated only by JetUpdate or and not during JetSetColumn. /// /// An error if the call fails. public int JetUpdate(JET_SESID sesid, JET_TABLEID tableid, byte[] bookmark, int bookmarkSize, out int actualBookmarkSize) { TraceFunctionCall(); #if MANAGEDESENT_ON_WSA return this.JetUpdate2(sesid, tableid, bookmark, bookmarkSize, out actualBookmarkSize, UpdateGrbit.None); #else CheckDataSize(bookmark, bookmarkSize, "bookmarkSize"); uint bytesActual; int err = Err(NativeMethods.JetUpdate(sesid.Value, tableid.Value, bookmark, checked((uint)bookmarkSize), out bytesActual)); actualBookmarkSize = GetActualSize(bytesActual); return err; #endif } /// /// The JetUpdate2 function performs an update operation including inserting a new row into /// a table or updating an existing row. Deleting a table row is performed by calling /// . /// /// The session which started the update. /// The cursor to update. An update should be prepared. /// Returns the bookmark of the updated record. This can be null. /// The size of the bookmark buffer. /// Returns the actual size of the bookmark. /// Update options. /// /// JetUpdate is the final step in performing an insert or an update. The update is begun by /// calling and then by calling /// JetSetColumn one or more times to set the record state. Finally, JetUpdate /// is called to complete the update operation. Indexes are updated only by JetUpdate or and not during JetSetColumn. /// /// An error if the call fails. public int JetUpdate2(JET_SESID sesid, JET_TABLEID tableid, byte[] bookmark, int bookmarkSize, out int actualBookmarkSize, UpdateGrbit grbit) { TraceFunctionCall(); CheckDataSize(bookmark, bookmarkSize, "bookmarkSize"); this.CheckSupportsServer2003Features("JetUpdate2"); uint bytesActual; int err = Err(NativeMethods.JetUpdate2(sesid.Value, tableid.Value, bookmark, checked((uint)bookmarkSize), out bytesActual, (uint)grbit)); actualBookmarkSize = GetActualSize(bytesActual); return err; } /// /// The JetSetColumn function modifies a single column value in a modified record to be inserted or to /// update the current record. It can overwrite an existing value, add a new value to a sequence of /// values in a multi-valued column, remove a value from a sequence of values in a multi-valued column, /// or update all or part of a long value (a column of type /// or ). /// /// /// The SetColumn methods provide datatype-specific overrides which may be more efficient. /// /// The session which is performing the update. /// The cursor to update. An update should be prepared. /// The columnid to set. /// The data to set. /// The size of data to set. /// SetColumn options. /// Used to specify itag or long-value offset. /// An error if the call fails. public int JetSetColumn(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, IntPtr data, int dataSize, SetColumnGrbit grbit, JET_SETINFO setinfo) { TraceFunctionCall(); CheckNotNegative(dataSize, "dataSize"); if (IntPtr.Zero == data) { if (dataSize > 0 && (SetColumnGrbit.SizeLV != (grbit & SetColumnGrbit.SizeLV))) { throw new ArgumentOutOfRangeException( "dataSize", dataSize, "cannot be greater than the length of the data (unless the SizeLV option is used)"); } } if (null != setinfo) { NATIVE_SETINFO nativeSetinfo = setinfo.GetNativeSetinfo(); return Err(NativeMethods.JetSetColumn(sesid.Value, tableid.Value, columnid.Value, data, checked((uint)dataSize), unchecked((uint)grbit), ref nativeSetinfo)); } return Err(NativeMethods.JetSetColumn(sesid.Value, tableid.Value, columnid.Value, data, checked((uint)dataSize), unchecked((uint)grbit), IntPtr.Zero)); } /// /// Allows an application to set multiple column values in a single /// operation. An array of structures is /// used to describe the set of column values to be set, and to describe /// input buffers for each column value to be set. /// /// The session to use. /// The cursor to set the columns on. /// /// An array of structures describing the /// data to set. /// /// /// Number of entries in the setcolumns parameter. /// /// An error code or warning. public unsafe int JetSetColumns(JET_SESID sesid, JET_TABLEID tableid, NATIVE_SETCOLUMN* setcolumns, int numColumns) { TraceFunctionCall(); return Err(NativeMethods.JetSetColumns(sesid.Value, tableid.Value, setcolumns, checked((uint)numColumns))); } #if !MANAGEDESENT_ON_WSA // Not exposed in MSDK /// /// Explicitly reserve the ability to update a row, write lock, or to explicitly prevent a row from /// being updated by any other session, read lock. Normally, row write locks are acquired implicitly as a /// result of updating rows. Read locks are usually not required because of record versioning. However, /// in some cases a transaction may desire to explicitly lock a row to enforce serialization, or to ensure /// that a subsequent operation will succeed. /// /// The session to use. /// The cursor to use. A lock will be acquired on the current record. /// Lock options, use this to specify which type of lock to obtain. /// An error if the call fails. public int JetGetLock(JET_SESID sesid, JET_TABLEID tableid, GetLockGrbit grbit) { TraceFunctionCall(); return Err(NativeMethods.JetGetLock(sesid.Value, tableid.Value, unchecked((uint)grbit))); } #endif // !MANAGEDESENT_ON_WSA /// /// Performs an atomic addition operation on one column. This function allows /// multiple sessions to update the same record concurrently without conflicts. /// /// The session to use. /// The cursor to update. /// /// The column to update. This must be an escrow updatable column. /// /// The buffer containing the addend. /// The size of the addend. /// /// An output buffer that will recieve the current value of the column. This buffer /// can be null. /// /// The size of the previousValue buffer. /// Returns the actual size of the previousValue. /// Escrow update options. /// An error code if the operation fails. public int JetEscrowUpdate( JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, byte[] delta, int deltaSize, byte[] previousValue, int previousValueLength, out int actualPreviousValueLength, EscrowUpdateGrbit grbit) { TraceFunctionCall(); CheckNotNull(delta, "delta"); CheckDataSize(delta, deltaSize, "deltaSize"); CheckDataSize(previousValue, previousValueLength, "previousValueLength"); uint bytesOldActual = 0; int err = Err(NativeMethods.JetEscrowUpdate( sesid.Value, tableid.Value, columnid.Value, delta, checked((uint)deltaSize), previousValue, checked((uint)previousValueLength), out bytesOldActual, unchecked((uint)grbit))); actualPreviousValueLength = checked((int)bytesOldActual); return err; } #endregion #region Callbacks /// /// Allows the application to configure the database engine to issue /// notifications to the application for specific events. These /// notifications are associated with a specific table and remain in /// effect only until the instance containing the table is shut down /// using . /// /// The session to use. /// /// A cursor opened on the table that the callback should be /// registered on. /// /// /// The callback reasons for which the application wishes to receive notifications. /// /// The callback function. /// A context that will be given to the callback. /// /// A handle that can later be used to cancel the registration of the given /// callback function using . /// /// An error if the call fails. public int JetRegisterCallback( JET_SESID sesid, JET_TABLEID tableid, JET_cbtyp cbtyp, JET_CALLBACK callback, IntPtr context, out JET_HANDLE callbackId) { TraceFunctionCall(); CheckNotNull(callback, "callback"); callbackId = JET_HANDLE.Nil; return Err(NativeMethods.JetRegisterCallback( sesid.Value, tableid.Value, unchecked((uint)cbtyp), this.callbackWrappers.Add(callback).NativeCallback, context, out callbackId.Value)); } /// /// Configures the database engine to stop issuing notifications to the /// application as previously requested through /// . /// /// The session to use. /// /// A cursor opened on the table that the callback should be /// registered on. /// /// /// The callback reasons for which the application no longer wishes to receive notifications. /// /// /// The handle of the registered callback that was returned by . /// /// An error if the call fails. public int JetUnregisterCallback(JET_SESID sesid, JET_TABLEID tableid, JET_cbtyp cbtyp, JET_HANDLE callbackId) { TraceFunctionCall(); this.callbackWrappers.Collect(); return Err(NativeMethods.JetUnregisterCallback( sesid.Value, tableid.Value, unchecked((uint)cbtyp), callbackId.Value)); } #endregion #region Online Maintenance /// /// Starts and stops database defragmentation tasks that improves data /// organization within a database. /// /// The session to use for the call. /// The database to be defragmented. /// /// Under some options defragmentation is performed for the entire database described by the given /// database ID, and other options (such as ) require /// the name of the table to defragment. /// /// /// When starting an online defragmentation task, this parameter sets the maximum number of defragmentation /// passes. When stopping an online defragmentation task, this parameter is set to the number of passes /// performed. This is not honored in all modes (such as ). /// /// /// When starting an online defragmentation task, this parameter sets /// the maximum time for defragmentation. When stopping an online /// defragmentation task, this output buffer is set to the length of /// time used for defragmentation. This is not honored in all modes (such as ). /// /// Defragmentation options. /// An error code. /// . /// . public int JetDefragment(JET_SESID sesid, JET_DBID dbid, string tableName, ref int passes, ref int seconds, DefragGrbit grbit) { #if MANAGEDESENT_ON_WSA return this.JetDefragment2(sesid, dbid, tableName, ref passes, ref seconds, null, grbit); #else TraceFunctionCall(); uint nativePasses = unchecked((uint)passes); uint nativeSeconds = unchecked((uint)seconds); int err = Err(NativeMethods.JetDefragment( sesid.Value, dbid.Value, tableName, ref nativePasses, ref nativeSeconds, (uint)grbit)); passes = unchecked((int)nativePasses); seconds = unchecked((int)nativeSeconds); return err; #endif } /// /// Starts and stops database defragmentation tasks that improves data /// organization within a database. /// /// The session to use for the call. /// The database to be defragmented. /// /// Under some options defragmentation is performed for the entire database described by the given /// database ID, and other options (such as ) require /// the name of the table to defragment. /// /// Defragmentation options. /// An error code. /// . public int Defragment( JET_SESID sesid, JET_DBID dbid, string tableName, DefragGrbit grbit) { #if MANAGEDESENT_ON_WSA return this.Defragment2(sesid, dbid, tableName, null, grbit); #else TraceFunctionCall(); int err = Err(NativeMethods.JetDefragment( sesid.Value, dbid.Value, tableName, IntPtr.Zero, IntPtr.Zero, (uint)grbit)); return err; #endif } /// /// Starts and stops database defragmentation tasks that improves data /// organization within a database. /// /// The session to use for the call. /// The database to be defragmented. /// /// Under some options defragmentation is performed for the entire database described by the given /// database ID, and other options (such as ) require /// the name of the table to defragment. /// /// /// When starting an online defragmentation task, this parameter sets the maximum number of defragmentation /// passes. When stopping an online defragmentation task, this parameter is set to the number of passes /// performed. This is not honored in all modes (such as ). /// /// /// When starting an online defragmentation task, this parameter sets /// the maximum time for defragmentation. When stopping an online /// defragmentation task, this output buffer is set to the length of /// time used for defragmentation. This is not honored in all modes (such as ). /// /// Callback function that defrag uses to report progress. /// Defragmentation options. /// An error code or warning. /// . public int JetDefragment2( JET_SESID sesid, JET_DBID dbid, string tableName, ref int passes, ref int seconds, JET_CALLBACK callback, DefragGrbit grbit) { TraceFunctionCall(); uint nativePasses = unchecked((uint)passes); uint nativeSeconds = unchecked((uint)seconds); IntPtr functionPointer; if (null == callback) { functionPointer = IntPtr.Zero; } else { JetCallbackWrapper callbackWrapper = this.callbackWrappers.Add(callback); functionPointer = Marshal.GetFunctionPointerForDelegate(callbackWrapper.NativeCallback); #if DEBUG GC.Collect(); #endif } #if MANAGEDESENT_ON_WSA int err = Err(NativeMethods.JetDefragment2W( sesid.Value, dbid.Value, tableName, ref nativePasses, ref nativeSeconds, functionPointer, (uint)grbit)); #else int err = Err(NativeMethods.JetDefragment2( sesid.Value, dbid.Value, tableName, ref nativePasses, ref nativeSeconds, functionPointer, (uint)grbit)); #endif passes = unchecked((int)nativePasses); seconds = unchecked((int)nativeSeconds); this.callbackWrappers.Collect(); return err; } //// Currently, this overload of JetDefragment2() is not used outside of WSA. #if MANAGEDESENT_ON_WSA /// /// Starts and stops database defragmentation tasks that improves data /// organization within a database. /// /// The session to use for the call. /// The database to be defragmented. /// /// Under some options defragmentation is performed for the entire database described by the given /// database ID, and other options (such as ) require /// the name of the table to defragment. /// /// Callback function that defrag uses to report progress. /// Defragmentation options. /// An error code or warning. /// . public int Defragment2( JET_SESID sesid, JET_DBID dbid, string tableName, JET_CALLBACK callback, DefragGrbit grbit) { TraceFunctionCall(); IntPtr functionPointer; if (null == callback) { functionPointer = IntPtr.Zero; } else { JetCallbackWrapper callbackWrapper = this.callbackWrappers.Add(callback); functionPointer = Marshal.GetFunctionPointerForDelegate(callbackWrapper.NativeCallback); #if DEBUG GC.Collect(); #endif } #if MANAGEDESENT_ON_WSA int err = Err(NativeMethods.JetDefragment2W( sesid.Value, dbid.Value, tableName, IntPtr.Zero, IntPtr.Zero, functionPointer, (uint)grbit)); #else int err = Err(NativeMethods.JetDefragment2( sesid.Value, dbid.Value, tableName, IntPtr.Zero, IntPtr.Zero, functionPointer, (uint)grbit)); #endif this.callbackWrappers.Collect(); return err; } #endif #if !MANAGEDESENT_ON_WSA // Not exposed in MSDK /// /// Performs idle cleanup tasks or checks the version store status in ESE. /// /// The session to use. /// A combination of JetIdleGrbit flags. /// An error code if the operation fails. public int JetIdle(JET_SESID sesid, IdleGrbit grbit) { TraceFunctionCall(); return Err(NativeMethods.JetIdle(sesid.Value, (uint)grbit)); } #endif // !MANAGEDESENT_ON_WSA #endregion #region Misc #if !MANAGEDESENT_ON_WSA // Not exposed in MSDK /// /// Crash dump options for Watson. /// /// Crash dump options. /// An error code. public int JetConfigureProcessForCrashDump(CrashDumpGrbit grbit) { TraceFunctionCall(); this.CheckSupportsWindows7Features("JetConfigureProcessForCrashDump"); return Err(NativeMethods.JetConfigureProcessForCrashDump((uint)grbit)); } /// /// Frees memory that was allocated by a database engine call. /// /// /// The buffer allocated by a call to the database engine. /// is acceptable, and will be ignored. /// /// An error code. public int JetFreeBuffer(IntPtr buffer) { TraceFunctionCall(); return Err(NativeMethods.JetFreeBuffer(buffer)); } #endif // !MANAGEDESENT_ON_WSA #endregion #region Internal Helper Methods /// /// Given the size returned by ESENT, get the size /// to return to the user. /// /// The size returned by ESENT. /// The bookmark size to return to the user. internal static int GetActualSize(uint numBytesActual) { // BUG: debug builds of ESENT can fill numBytesActual with this value in case of failure. const uint CbActualDebugFill = 0xDDDDDDDD; int actualSize; if (CbActualDebugFill == numBytesActual) { actualSize = 0; } else { actualSize = checked((int)numBytesActual); } return actualSize; } #endregion Internal Helper Methods #region Parameter Checking and Tracing /// /// Make sure the data, dataOffset and dataSize arguments match. /// /// The data buffer. /// The offset into the data. /// The name of the offset argument. /// The size of the data. /// The name of the size argument. /// The type of the data. private static void CheckDataSize(ICollection data, int dataOffset, string offsetArgumentName, int dataSize, string sizeArgumentName) { CheckNotNegative(dataSize, sizeArgumentName); CheckNotNegative(dataOffset, offsetArgumentName); if ((null == data && 0 != dataOffset) || (null != data && dataOffset > data.Count)) { Trace.WriteLineIf(TraceSwitch.TraceError, "CheckDataSize failed"); throw new ArgumentOutOfRangeException( offsetArgumentName, dataOffset, "cannot be greater than the length of the buffer"); } if ((null == data && 0 != dataSize) || (null != data && dataSize > data.Count - dataOffset)) { Trace.WriteLineIf(TraceSwitch.TraceError, "CheckDataSize failed"); throw new ArgumentOutOfRangeException( sizeArgumentName, dataSize, "cannot be greater than the length of the buffer"); } } /// /// Make sure the data and dataSize arguments match. /// /// The data buffer. /// The size of the data. /// The name of the size argument. /// The type of the data. private static void CheckDataSize(ICollection data, int dataSize, string argumentName) { CheckDataSize(data, 0, string.Empty, dataSize, argumentName); } /// /// Make sure the given object isn't null. If it is /// then throw an ArgumentNullException. /// /// The object to check. /// The name of the parameter. private static void CheckNotNull(object o, string paramName) { if (null == o) { Trace.WriteLineIf(TraceSwitch.TraceError, "CheckNotNull failed"); throw new ArgumentNullException(paramName); } } /// /// Make sure the given integer isn't negative. If it is /// then throw an ArgumentOutOfRangeException. /// /// The integer to check. /// The name of the parameter. private static void CheckNotNegative(long i, string paramName) { if (i < 0) { Trace.WriteLineIf(TraceSwitch.TraceError, "CheckNotNegative failed"); throw new ArgumentOutOfRangeException(paramName, i, "cannot be negative"); } } /// /// Used when an unsupported API method is called. This /// logs an error and returns an InvalidOperationException. /// /// The name of the method. /// The exception to throw. private static Exception UnsupportedApiException(string method) { string error = string.Format(CultureInfo.InvariantCulture, "Method {0} is not supported by this version of ESENT", method); Trace.WriteLineIf(TraceSwitch.TraceError, error); return new InvalidOperationException(error); } /// /// Trace a call to an ESENT function. /// /// The name of the function being called. [Conditional("TRACE")] #if DEBUG // Disallow inlining so we can always get the name of the calling function. [MethodImpl(MethodImplOptions.NoInlining)] #endif private static void TraceFunctionCall([System.Runtime.CompilerServices.CallerMemberName] string function = null) { Trace.WriteLineIf(TraceSwitch.TraceInfo, function); } /// /// Can be used to trap ESENT errors. /// /// The error being returned. /// The error. private static int Err(int err) { TraceErr(err); return err; } /// /// Trace an error generated by a call to ESENT. /// /// The error to trace. [Conditional("TRACE")] private static void TraceErr(int err) { if (0 == err) { Trace.WriteLineIf(TraceSwitch.TraceVerbose, "JET_err.Success"); } else if (err > 0) { Trace.WriteLineIf(TraceSwitch.TraceWarning, unchecked((JET_wrn)err)); } else { Trace.WriteLineIf(TraceSwitch.TraceError, unchecked((JET_err)err)); } } #endregion Parameter Checking and Tracing #region Helper Methods /// /// Convert managed JET_ENUMCOLUMNID objects to NATIVE_ENUMCOLUMNID /// structures. /// /// The columnids to convert. /// The number of columnids to convert. /// The array to store the converted columnids. /// The total number of tag entries in the converted structures. private static unsafe int ConvertEnumColumnids(IList columnids, int numColumnids, NATIVE_ENUMCOLUMNID* nativecolumnids) { int totalNumTags = 0; for (int i = 0; i < numColumnids; ++i) { nativecolumnids[i] = columnids[i].GetNativeEnumColumnid(); checked { totalNumTags += columnids[i].ctagSequence; } } return totalNumTags; } /// /// Convert managed rgtagSequence to unmanaged rgtagSequence. /// /// The columnids to convert. /// The number of columnids to covert. /// The unmanaged columnids to add the tags to. /// /// Memory to use for converted rgtagSequence. This should be large enough to /// hold all columnids. /// private static unsafe void ConvertEnumColumnidTags(IList columnids, int numColumnids, NATIVE_ENUMCOLUMNID* nativecolumnids, uint* tags) { for (int i = 0; i < numColumnids; ++i) { nativecolumnids[i].rgtagSequence = tags; for (int j = 0; j < columnids[i].ctagSequence; ++j) { nativecolumnids[i].rgtagSequence[j] = checked((uint)columnids[i].rgtagSequence[j]); } tags += columnids[i].ctagSequence; } } /// /// Convert the native (unmanaged) results of JetEnumerateColumns to /// managed objects. This uses the allocator callback to free some /// memory as the data is converted. /// /// The allocator callback used. /// The allocator callback context. /// Number of NATIVE_ENUMCOLUMN structures returned. /// NATIVE_ENUMCOLUMN structures. /// Returns the number of converted JET_ENUMCOLUMN objects. /// Returns the convertd column values. private static unsafe void ConvertEnumerateColumnsResult(JET_PFNREALLOC allocator, IntPtr allocatorContext, uint numEnumColumn, NATIVE_ENUMCOLUMN* nativeenumcolumns, out int numColumnValues, out JET_ENUMCOLUMN[] columnValues) { numColumnValues = checked((int)numEnumColumn); columnValues = new JET_ENUMCOLUMN[numColumnValues]; for (int i = 0; i < numColumnValues; ++i) { columnValues[i] = new JET_ENUMCOLUMN(); columnValues[i].SetFromNativeEnumColumn(nativeenumcolumns[i]); if (JET_wrn.ColumnSingleValue != columnValues[i].err) { columnValues[i].rgEnumColumnValue = new JET_ENUMCOLUMNVALUE[columnValues[i].cEnumColumnValue]; for (int j = 0; j < columnValues[i].cEnumColumnValue; ++j) { columnValues[i].rgEnumColumnValue[j] = new JET_ENUMCOLUMNVALUE(); columnValues[i].rgEnumColumnValue[j].SetFromNativeEnumColumnValue(nativeenumcolumns[i].rgEnumColumnValue[j]); } // the NATIVE_ENUMCOLUMNVALUES have been converted // free their memory allocator(allocatorContext, new IntPtr(nativeenumcolumns[i].rgEnumColumnValue), 0); nativeenumcolumns[i].rgEnumColumnValue = null; } } // Now we have converted all the NATIVE_ENUMCOLUMNS we can // free the memory they use allocator(allocatorContext, new IntPtr(nativeenumcolumns), 0); nativeenumcolumns = null; } /// /// Make an array of native columndefs from JET_COLUMNDEFs. /// /// Columndefs to convert. /// Number of columndefs to convert. /// An array of native columndefs. private static NATIVE_COLUMNDEF[] GetNativecolumndefs(IList columns, int numColumns) { var nativecolumndefs = new NATIVE_COLUMNDEF[numColumns]; for (int i = 0; i < numColumns; ++i) { nativecolumndefs[i] = columns[i].GetNativeColumndef(); } return nativecolumndefs; } /// /// Make native conditionalcolumn structures from the managed ones. /// /// The conditional columns to convert. /// Wehether to convert the strings with UTF-16. /// The handle collection used to pin the data. /// Pinned native versions of the conditional columns. private static IntPtr GetNativeConditionalColumns( IList conditionalColumns, bool useUnicodeData, ref GCHandleCollection handles) { if (null == conditionalColumns) { return IntPtr.Zero; } var nativeConditionalcolumns = new NATIVE_CONDITIONALCOLUMN[conditionalColumns.Count]; for (int i = 0; i < conditionalColumns.Count; ++i) { nativeConditionalcolumns[i] = conditionalColumns[i].GetNativeConditionalColumn(); if (useUnicodeData) { nativeConditionalcolumns[i].szColumnName = handles.Add(Util.ConvertToNullTerminatedUnicodeByteArray(conditionalColumns[i].szColumnName)); } else { nativeConditionalcolumns[i].szColumnName = handles.Add(Util.ConvertToNullTerminatedAsciiByteArray(conditionalColumns[i].szColumnName)); } } return handles.Add(nativeConditionalcolumns); } /// /// Make native columncreate structures from the managed ones. /// /// Column create structures to convert. /// Wehether to convert the strings with UTF-16. /// The handle collection used to pin the data. /// Pinned native versions of the column creates. private static IntPtr GetNativeColumnCreates( IList managedColumnCreates, bool useUnicodeData, ref GCHandleCollection handles) { IntPtr nativeBuffer = IntPtr.Zero; if (managedColumnCreates != null && managedColumnCreates.Count > 0) { var nativeColumns = new NATIVE_COLUMNCREATE[managedColumnCreates.Count]; for (int i = 0; i < managedColumnCreates.Count; ++i) { if (managedColumnCreates[i] != null) { JET_COLUMNCREATE managedColumn = managedColumnCreates[i]; nativeColumns[i] = managedColumn.GetNativeColumnCreate(); if (useUnicodeData) { nativeColumns[i].szColumnName = handles.Add(Util.ConvertToNullTerminatedUnicodeByteArray(managedColumn.szColumnName)); } else { nativeColumns[i].szColumnName = handles.Add(Util.ConvertToNullTerminatedAsciiByteArray(managedColumn.szColumnName)); } if (managedColumn.cbDefault > 0) { nativeColumns[i].pvDefault = handles.Add(managedColumn.pvDefault); } } } nativeBuffer = handles.Add(nativeColumns); } return nativeBuffer; } #if !MANAGEDESENT_ON_WSA // Not exposed in MSDK /// /// Make native indexcreate structures from the managed ones. /// Only supports Ascii data, since it could be used on XP. /// /// Index create structures to convert. /// The handle collection used to pin the data. /// Pinned native versions of the index creates. private static unsafe JET_INDEXCREATE.NATIVE_INDEXCREATE[] GetNativeIndexCreates( IList managedIndexCreates, ref GCHandleCollection handles) { JET_INDEXCREATE.NATIVE_INDEXCREATE[] nativeIndices = null; if (managedIndexCreates != null && managedIndexCreates.Count > 0) { nativeIndices = new JET_INDEXCREATE.NATIVE_INDEXCREATE[managedIndexCreates.Count]; for (int i = 0; i < managedIndexCreates.Count; ++i) { nativeIndices[i] = managedIndexCreates[i].GetNativeIndexcreate(); if (null != managedIndexCreates[i].pidxUnicode) { NATIVE_UNICODEINDEX unicode = managedIndexCreates[i].pidxUnicode.GetNativeUnicodeIndex(); nativeIndices[i].pidxUnicode = (NATIVE_UNICODEINDEX*)handles.Add(unicode); nativeIndices[i].grbit |= (uint)VistaGrbits.IndexUnicode; } nativeIndices[i].szKey = handles.Add(Util.ConvertToNullTerminatedAsciiByteArray(managedIndexCreates[i].szKey)); nativeIndices[i].szIndexName = handles.Add(Util.ConvertToNullTerminatedAsciiByteArray(managedIndexCreates[i].szIndexName)); nativeIndices[i].rgconditionalcolumn = GetNativeConditionalColumns(managedIndexCreates[i].rgconditionalcolumn, false, ref handles); } } return nativeIndices; } /// /// Make native indexcreate structures from the managed ones. /// Only supports Unicode data, since it was introduced in Vista. /// /// Index create structures to convert. /// The handle collection used to pin the data. /// Pinned native versions of the index creates. private static unsafe JET_INDEXCREATE.NATIVE_INDEXCREATE1[] GetNativeIndexCreate1s( IList managedIndexCreates, ref GCHandleCollection handles) { JET_INDEXCREATE.NATIVE_INDEXCREATE1[] nativeIndices = null; if (managedIndexCreates != null && managedIndexCreates.Count > 0) { nativeIndices = new JET_INDEXCREATE.NATIVE_INDEXCREATE1[managedIndexCreates.Count]; for (int i = 0; i < managedIndexCreates.Count; ++i) { nativeIndices[i] = managedIndexCreates[i].GetNativeIndexcreate1(); if (null != managedIndexCreates[i].pidxUnicode) { NATIVE_UNICODEINDEX unicode = managedIndexCreates[i].pidxUnicode.GetNativeUnicodeIndex(); nativeIndices[i].indexcreate.pidxUnicode = (NATIVE_UNICODEINDEX*)handles.Add(unicode); nativeIndices[i].indexcreate.grbit |= (uint)VistaGrbits.IndexUnicode; } nativeIndices[i].indexcreate.szKey = handles.Add(Util.ConvertToNullTerminatedUnicodeByteArray(managedIndexCreates[i].szKey)); nativeIndices[i].indexcreate.cbKey *= sizeof(char); nativeIndices[i].indexcreate.szIndexName = handles.Add(Util.ConvertToNullTerminatedUnicodeByteArray(managedIndexCreates[i].szIndexName)); nativeIndices[i].indexcreate.rgconditionalcolumn = GetNativeConditionalColumns(managedIndexCreates[i].rgconditionalcolumn, false, ref handles); } } return nativeIndices; } /// /// Make native indexcreate structures from the managed ones. /// Only supports Unicode data, since it was introduced in Win7. /// /// Index create structures to convert. /// The handle collection used to pin the data. /// Pinned native versions of the index creates. private static unsafe JET_INDEXCREATE.NATIVE_INDEXCREATE2[] GetNativeIndexCreate2s( IList managedIndexCreates, ref GCHandleCollection handles) { JET_INDEXCREATE.NATIVE_INDEXCREATE2[] nativeIndices = null; if (managedIndexCreates != null && managedIndexCreates.Count > 0) { nativeIndices = new JET_INDEXCREATE.NATIVE_INDEXCREATE2[managedIndexCreates.Count]; for (int i = 0; i < managedIndexCreates.Count; ++i) { nativeIndices[i] = managedIndexCreates[i].GetNativeIndexcreate2(); if (null != managedIndexCreates[i].pidxUnicode) { NATIVE_UNICODEINDEX unicode = managedIndexCreates[i].pidxUnicode.GetNativeUnicodeIndex(); nativeIndices[i].indexcreate1.indexcreate.pidxUnicode = (NATIVE_UNICODEINDEX*)handles.Add(unicode); nativeIndices[i].indexcreate1.indexcreate.grbit |= (uint)VistaGrbits.IndexUnicode; } nativeIndices[i].indexcreate1.indexcreate.szKey = handles.Add(Util.ConvertToNullTerminatedUnicodeByteArray(managedIndexCreates[i].szKey)); nativeIndices[i].indexcreate1.indexcreate.cbKey *= sizeof(char); nativeIndices[i].indexcreate1.indexcreate.szIndexName = handles.Add(Util.ConvertToNullTerminatedUnicodeByteArray(managedIndexCreates[i].szIndexName)); nativeIndices[i].indexcreate1.indexcreate.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; } #endif // !MANAGEDESENT_ON_WSA /// /// Set managed columnids from unmanaged columnids. This also sets the columnids /// in the columndefs. /// /// The column definitions. /// The columnids to set. /// The native columnids. /// The number of columnids to set. private static void SetColumnids(IList columns, IList columnids, IList nativecolumnids, int numColumns) { for (int i = 0; i < numColumns; ++i) { columnids[i] = new JET_COLUMNID { Value = nativecolumnids[i] }; columns[i].columnid = columnids[i]; } } #if !MANAGEDESENT_ON_WSA // Not exposed in MSDK /// /// 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 CreateIndexes(JET_SESID sesid, JET_TABLEID tableid, IList indexcreates, int numIndexCreates) { // pin the memory var handles = new GCHandleCollection(); try { JET_INDEXCREATE.NATIVE_INDEXCREATE[] nativeIndexcreates = GetNativeIndexCreates(indexcreates, ref handles); return Err(NativeMethods.JetCreateIndex2(sesid.Value, tableid.Value, nativeIndexcreates, checked((uint)numIndexCreates))); } finally { handles.Dispose(); } } /// /// 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 CreateIndexes1(JET_SESID sesid, JET_TABLEID tableid, IList indexcreates, int numIndexCreates) { // pin the memory var handles = new GCHandleCollection(); try { JET_INDEXCREATE.NATIVE_INDEXCREATE1[] nativeIndexcreates = GetNativeIndexCreate1s(indexcreates, ref handles); return Err(NativeMethods.JetCreateIndex2W(sesid.Value, tableid.Value, nativeIndexcreates, checked((uint)numIndexCreates))); } finally { handles.Dispose(); } } /// /// 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 CreateIndexes2(JET_SESID sesid, JET_TABLEID tableid, IList indexcreates, int numIndexCreates) { // pin the memory var handles = new GCHandleCollection(); try { JET_INDEXCREATE.NATIVE_INDEXCREATE2[] nativeIndexcreates = GetNativeIndexCreate2s(indexcreates, ref handles); return Err(NativeMethods.JetCreateIndex3W(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 CreateTableColumnIndex3( JET_SESID sesid, JET_DBID dbid, JET_TABLECREATE tablecreate) { JET_TABLECREATE.NATIVE_TABLECREATE3 nativeTableCreate = tablecreate.GetNativeTableCreate3(); 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. JET_INDEXCREATE.NATIVE_INDEXCREATE2[] nativeIndexCreates = GetNativeIndexCreate2s(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.JetCreateTableColumnIndex3W(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(nativeIndexCreates[i]); } } return Err(err); } finally { handles.Dispose(); } } } /// /// Convert native instance info structures to managed, treating the /// unmanaged strings as Unicode. /// /// The number of native structures. /// /// A pointer to the native structures. This pointer will be freed with JetFreeBuffer. /// /// /// An array of JET_INSTANCE_INFO structures created from the unmanaged. /// private unsafe JET_INSTANCE_INFO[] ConvertInstanceInfosUnicode(uint nativeNumInstance, NATIVE_INSTANCE_INFO* nativeInstanceInfos) { int numInstances = checked((int)nativeNumInstance); var instances = new JET_INSTANCE_INFO[numInstances]; for (int i = 0; i < numInstances; ++i) { instances[i] = new JET_INSTANCE_INFO(); instances[i].SetFromNativeUnicode(nativeInstanceInfos[i]); } this.JetFreeBuffer(new IntPtr(nativeInstanceInfos)); return instances; } /// /// Convert native instance info structures to managed, treating the /// unmanaged string as Unicode. /// /// The number of native structures. /// /// A pointer to the native structures. This pointer will be freed with JetFreeBuffer. /// /// /// An array of JET_INSTANCE_INFO structures created from the unmanaged. /// private unsafe JET_INSTANCE_INFO[] ConvertInstanceInfosAscii(uint nativeNumInstance, NATIVE_INSTANCE_INFO* nativeInstanceInfos) { int numInstances = checked((int)nativeNumInstance); var instances = new JET_INSTANCE_INFO[numInstances]; for (int i = 0; i < numInstances; ++i) { instances[i] = new JET_INSTANCE_INFO(); instances[i].SetFromNativeAscii(nativeInstanceInfos[i]); } this.JetFreeBuffer(new IntPtr(nativeInstanceInfos)); return instances; } #endif // !MANAGEDESENT_ON_WSA #endregion #region Capability Checking /// /// Check that ESENT supports Server 2003 features. Throws an exception if Server 2003 features /// aren't supported. /// /// The API that is being called. private void CheckSupportsServer2003Features(string api) { if (!this.Capabilities.SupportsServer2003Features) { throw UnsupportedApiException(api); } } /// /// Check that ESENT supports Vista features. Throws an exception if Vista features /// aren't supported. /// /// The API that is being called. private void CheckSupportsVistaFeatures(string api) { if (!this.Capabilities.SupportsVistaFeatures) { throw UnsupportedApiException(api); } } /// /// Check that ESENT supports Windows7 features. Throws an exception if Windows7 features /// aren't supported. /// /// The API that is being called. private void CheckSupportsWindows7Features(string api) { if (!this.Capabilities.SupportsWindows7Features) { throw UnsupportedApiException(api); } } /// /// Check that ESENT supports Windows8 features. Throws an exception if Windows8 features /// aren't supported. /// /// The API that is being called. private void CheckSupportsWindows8Features(string api) { if (!this.Capabilities.SupportsWindows8Features) { throw UnsupportedApiException(api); } } /// /// Check that ESENT supports Windows10 features. Throws an exception if Windows10 features /// aren't supported. /// /// The API that is being called. private void CheckSupportsWindows10Features(string api) { if (!this.Capabilities.SupportsWindows10Features) { throw UnsupportedApiException(api); } } #endregion #region Non-static Helper Methods // This overload takes an IntPtr rather than a JET_INDEXID. It's meant to only be called by // our JetSetCurrentIndex1-3, to 'up-cast' to JetSetCurrentIndex4(). #if MANAGEDESENT_ON_WSA /// /// Set the current index of a cursor. /// This overload takes an IntPtr rather than a JET_INDEXID. It's meant to only be called by /// our JetSetCurrentIndex1-3, to 'up-cast' to JetSetCurrentIndex4(). /// /// The session to use. /// The cursor to set the index on. /// /// The name of the index to be selected. If this is null or empty the primary /// index will be selected. /// /// /// Reserved. Must be IntPtr.Zero. /// /// /// Set index options. /// /// /// Sequence number of the multi-valued column value which will be used /// to position the cursor on the new index. This parameter is only used /// in conjunction with . When /// this parameter is not present or is set to zero, its value is presumed /// to be 1. /// /// An error if the call fails. private int JetSetCurrentIndex4( JET_SESID sesid, JET_TABLEID tableid, string index, IntPtr indexid, SetCurrentIndexGrbit grbit, int itagSequence) { TraceFunctionCall(); if (indexid != IntPtr.Zero) { // If you have a valid indexid, you should be using the other overload! throw new ArgumentException("indexid must be IntPtr.Zero.", "indexid"); } // A null index name is valid here -- it will set the table to the primary index return Err(NativeMethods.JetSetCurrentIndex4W(sesid.Value, tableid.Value, index, indexid, (uint)grbit, checked((uint)itagSequence))); } #endif // MANAGEDESENT_ON_WSA #if !MANAGEDESENT_ON_WSA // Not exposed in MSDK /// /// 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 int CreateTableColumnIndex2( JET_SESID sesid, JET_DBID dbid, JET_TABLECREATE tablecreate) { JET_TABLECREATE.NATIVE_TABLECREATE2 nativeTableCreate = tablecreate.GetNativeTableCreate2(); unsafe { var handles = new GCHandleCollection(); try { JET_INDEXCREATE.NATIVE_INDEXCREATE1[] nativeIndexCreate1s = null; JET_INDEXCREATE.NATIVE_INDEXCREATE[] nativeIndexCreates = null; int err; if (this.Capabilities.SupportsVistaFeatures) { // Convert/pin the column definitions. nativeTableCreate.rgcolumncreate = (NATIVE_COLUMNCREATE*)GetNativeColumnCreates(tablecreate.rgcolumncreate, true, ref handles); // Convert/pin the index definitions. nativeIndexCreate1s = GetNativeIndexCreate1s(tablecreate.rgindexcreate, ref handles); nativeTableCreate.rgindexcreate = handles.Add(nativeIndexCreate1s); err = NativeMethods.JetCreateTableColumnIndex2W(sesid.Value, dbid.Value, ref nativeTableCreate); } else { // Convert/pin the column definitions. nativeTableCreate.rgcolumncreate = (NATIVE_COLUMNCREATE*)GetNativeColumnCreates(tablecreate.rgcolumncreate, false, ref handles); // Convert/pin the index definitions. nativeIndexCreates = GetNativeIndexCreates(tablecreate.rgindexcreate, ref handles); nativeTableCreate.rgindexcreate = handles.Add(nativeIndexCreates); err = NativeMethods.JetCreateTableColumnIndex2(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) { if (null != nativeIndexCreate1s) { tablecreate.rgindexcreate[i].SetFromNativeIndexCreate(nativeIndexCreate1s[i]); } else { tablecreate.rgindexcreate[i].SetFromNativeIndexCreate(nativeIndexCreates[i]); } } } return Err(err); } finally { handles.Dispose(); } } } #endif // !MANAGEDESENT_ON_WSA #endregion #region Hooks for not-yet-published APIs. /// /// Provides a hook to allow population of additional fields in /// a different file. These additonal fields are not yet published /// on MSDN. /// /// The name of the database about which to retrieve information. /// The output structure to populate. /// Specifies which information to retrieve. /// Whether the additional fields specified by in /// are populated. /// The error code returned. partial void NotYetPublishedGetDbinfomisc( string databaseName, ref JET_DBINFOMISC dbinfomisc, JET_DbInfo infoLevel, ref bool notYetPublishedSupported, ref int err); /// /// Provides a hook to allow population of additional fields in /// a different file. These additonal fields are not yet published /// on MSDN. /// /// The session to use. /// The database identifier. /// The output structure to populate. /// Specifies which information to retrieve. /// Whether the additional fields specified by in /// are populated. /// The error code returned. partial void NotYetPublishedGetDbinfomisc( JET_SESID sesid, JET_DBID dbid, ref JET_DBINFOMISC dbinfomisc, JET_DbInfo infoLevel, ref bool notYetPublishedSupported, ref int err); #endregion } }