//----------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. // //----------------------------------------------------------------------- namespace Microsoft.Database.Isam.Config { using System; using System.Diagnostics.Contracts; using System.Globalization; using System.Reflection; using System.Threading; using Microsoft.Database.Isam.Win32; using Microsoft.Isam.Esent.Interop; using Microsoft.Isam.Esent.Interop.Vista; using Microsoft.Isam.Esent.Interop.Windows10; /// /// Represents a subset of JET_param definitions required for config classes. /// internal struct ParamDef { /// /// JET_param Id. /// public int Id; /// /// Can be set after global init? /// public bool StaticAfterGlobalInit; /// /// Managed type of the parameter. /// public Type ParamType; /// /// Initializes a new instance of the ParamDef struct. /// /// Id. /// Can be set after global init? /// Managed type of the param. public ParamDef(int id, bool staticAfterGlobalInit, Type paramType) { this.Id = id; this.StaticAfterGlobalInit = staticAfterGlobalInit; this.ParamType = paramType; } } /// /// A class that contains all engine wide Ese parameters. /// public sealed partial class DatabaseConfig : ConfigSetBase { /// /// A table of JET_param definitions used by config classes. /// internal static readonly ParamDef[] ParamTable; /// /// Passed to JetGetSystemParameter for retrieving string parameters. /// private const int MaxStringParamSize = 260; /// /// A per-process lock to synchronize calls to SetGlobalParams() /// private static readonly Mutex GlobalParamsMutex; /// /// Initializes static members of the class. /// Used to initialize the param table. /// static DatabaseConfig() { // Create a per-process named mutex. C# locks are per app-domain, which could potentially screw up Ese global params that are process wide. DatabaseConfig.GlobalParamsMutex = new Mutex(false, "Microsoft.Isam.Esent.Interop.DatabaseConfig.globalParamsMutex_" + NativeMethods.GetCurrentProcessId()); DatabaseConfig.ParamTable = new ParamDef[DatabaseConfig.ParamMaxValueInvalid]; FillParamTable(); UnpublishedFillParamTable(); } /// /// Gets or sets a string identifier that uniquely identifies an instance of Database. /// public string Identifier { get { return this.GetEngineParam(DatabaseParams.Identifier); } set { this.SetEngineParam(DatabaseParams.Identifier, value); } } /// /// Gets or sets a user-friendly name that is used to identify an instance of Database in system diagnostics (event log, etc). /// public string DisplayName { get { return this.GetEngineParam(DatabaseParams.DisplayName); } set { this.SetEngineParam(DatabaseParams.DisplayName, value); } } /// /// Gets or sets flags used to select optional Engine behaviour at initialization. See and /// public CreateInstanceGrbit EngineFlags { get { return this.GetEngineParam(DatabaseParams.EngineFlags); } set { this.SetEngineParam(DatabaseParams.EngineFlags, value); } } /// /// Gets or sets flags used to select optional behaviour while initializing an instance in the Engine and recovering the database from the log stream. See and /// public InitGrbit DatabaseRecoveryFlags { get { return this.GetEngineParam(DatabaseParams.DatabaseRecoveryFlags); } set { this.SetEngineParam(DatabaseParams.DatabaseRecoveryFlags, value); } } /// /// Gets or sets a path to use for creating or opening the database file. /// public string DatabaseFilename { get { return this.GetEngineParam(DatabaseParams.DatabaseFilename); } set { this.SetEngineParam(DatabaseParams.DatabaseFilename, value); } } /// /// Gets or sets flags used to select optional behaviour while creating a new database file. See and /// public CreateDatabaseGrbit DatabaseCreationFlags { get { return this.GetEngineParam(DatabaseParams.DatabaseCreationFlags); } set { this.SetEngineParam(DatabaseParams.DatabaseCreationFlags, value); } } /// /// Gets or sets flags used to select optional behaviour while attaching a database file to the Engine. See and /// public AttachDatabaseGrbit DatabaseAttachFlags { get { return this.GetEngineParam(DatabaseParams.DatabaseAttachFlags); } set { this.SetEngineParam(DatabaseParams.DatabaseAttachFlags, value); } } /// /// Gets or sets flags used to select optional behaviour while terminating an instance in the Engine. See and /// public TermGrbit DatabaseStopFlags { get { return this.GetEngineParam(DatabaseParams.DatabaseStopFlags); } set { this.SetEngineParam(DatabaseParams.DatabaseStopFlags, value); } } /// /// Gets or sets the maximum size of the database in pages. Zero means there is no maximum. See maxPages parameter for or /// public int DatabaseMaxPages { get { return this.GetEngineParam(DatabaseParams.DatabaseMaxPages); } set { this.SetEngineParam(DatabaseParams.DatabaseMaxPages, value); } } /// /// Sets global parameters before an Ese instance is created. /// public void SetGlobalParams() { // Lock this section using a process-wide lock, so in case of a race between two threads, a config set is either completely set or completely over-written DatabaseConfig.GlobalParamsMutex.WaitOne(); try { for (int i = 0; i < DatabaseConfig.ParamTable.Length; i++) { // This check ensures that only global params that must be set before global init are set here // This protects instance params from being set before instance init. Setting them here would change the defaults for these params // for all future instances. That would be really bad because the settings contained in the current config set would 'spill over' // into future config sets. if (DatabaseConfig.ParamTable[i].StaticAfterGlobalInit) { object valueToSet; if (this.ParamStore.TryGetValue(i, out valueToSet)) { object currValue; DatabaseConfig.TryGetParamFromInstance(JET_INSTANCE.Nil, i, out currValue); if (!currValue.Equals(valueToSet)) { DatabaseConfig.SetParamOnInstance(JET_INSTANCE.Nil, i, valueToSet); } } } } } finally { DatabaseConfig.GlobalParamsMutex.ReleaseMutex(); } } /// /// Sets instance specific parameters after an Ese instance has been created. /// /// The instance handle to use. public void SetInstanceParams(JET_INSTANCE instance) { Action setParamIfExists = (engineConfig, inst, param) => { object value; if (engineConfig.ParamStore.TryGetValue(param, out value)) { DatabaseConfig.SetParamOnInstance(inst, param, value); } }; // First, set parameters that require to be set in a particular order #if !ESENT && !MANAGEDESENT_ON_WSA setParamIfExists(this, instance, (int)Windows10Param.ConfigStoreSpec); #endif setParamIfExists(this, instance, (int)VistaParam.EnableAdvanced); setParamIfExists(this, instance, (int)VistaParam.Configuration); for (int i = 0; i < DatabaseConfig.ParamTable.Length; i++) { if (!DatabaseConfig.ParamTable[i].StaticAfterGlobalInit #if !ESENT && !MANAGEDESENT_ON_WSA && i != (int)Windows10Param.ConfigStoreSpec #endif && i != (int)VistaParam.EnableAdvanced && i != (int)VistaParam.Configuration) { setParamIfExists(this, instance, i); } } } /// /// Returns a that represents the current . /// /// /// A that represents the current . /// public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "DatabaseConfig( {0} ) [Id: {1}]", this.DisplayName, this.Identifier); } /// /// Get a from a particular instance. /// /// The instance handle to use. /// The param id. /// The param value. /// True for all instance params. False otherwise. internal static bool TryGetParamFromInstance(JET_INSTANCE instance, int param, out object value) { value = null; if (param >= DatabaseConfig.ParamMaxValueInvalid) { return false; } JET_wrn wrn; var paramDef = DatabaseConfig.ParamTable[param]; if (paramDef.ParamType == typeof(string)) { string strValue; var intValue = new IntPtr(0); wrn = Api.JetGetSystemParameter(instance, JET_SESID.Nil, (JET_param)param, ref intValue, out strValue, DatabaseConfig.MaxStringParamSize); value = strValue; } else { string strValue; var intValue = new IntPtr(0); wrn = Api.JetGetSystemParameter(instance, JET_SESID.Nil, (JET_param)param, ref intValue, out strValue, 0); if (paramDef.ParamType == typeof(int)) { value = intValue.ToInt32(); } else if (paramDef.ParamType == typeof(bool)) { value = Convert.ToBoolean(intValue.ToInt32()); } else if (paramDef.ParamType.IsEnum) { value = Enum.ToObject(paramDef.ParamType, intValue.ToInt32()); } else if (paramDef.ParamType == typeof(IntPtr)) { value = intValue; } else { throw new NotSupportedException(string.Format("Can't get param {0}. Type {1} isn't supported.", paramDef.Id, paramDef.ParamType)); } } Contract.Ensures(wrn == JET_wrn.Success); return true; } /// /// Sets a on a particular instance. /// /// The instance handle to use. /// The param id. /// The param value. internal static void SetParamOnInstance(JET_INSTANCE instance, int param, object value) { Contract.Requires(param < DatabaseConfig.ParamMaxValueInvalid); JET_wrn wrn; var paramDef = DatabaseConfig.ParamTable[param]; Contract.Requires(paramDef.ParamType == value.GetType()); if (paramDef.ParamType == typeof(string)) { wrn = Api.JetSetSystemParameter(instance, JET_SESID.Nil, (JET_param)param, 0, (string)value); } else { IntPtr intValue; if (paramDef.ParamType == typeof(int) || paramDef.ParamType.IsEnum) { intValue = new IntPtr((int)value); } else if (paramDef.ParamType == typeof(bool)) { intValue = new IntPtr(Convert.ToInt32((bool)value)); } else if (paramDef.ParamType == typeof(IntPtr)) { intValue = (IntPtr)value; } else { throw new NotSupportedException(string.Format("Can't set param {0}. Type {1} isn't supported.", paramDef.Id, paramDef.ParamType)); } wrn = Api.JetSetSystemParameter(instance, JET_SESID.Nil, (JET_param)param, intValue, null); } Contract.Ensures(wrn == JET_wrn.Success); } /// /// Partial method for filling the param table from auto-generated code. /// static partial void FillParamTable(); /// /// Partial method for filling the param table from auto-generated code. /// static partial void UnpublishedFillParamTable(); /// /// Helper method to get a custom defined engine param. /// /// The type of the engine param. /// The engine param to get. /// The engine param value. private T GetEngineParam(DatabaseParams param) { // Engine params are always accessed from the config store dictionary (even if the config set is live) return ConfigSetBase.GetValueOrDefault(this.ParamStore.TryGetValue, (int)param); } /// /// Helper method to set a custom defined engine param. /// /// The engine param to set. /// The engine param value. private void SetEngineParam(DatabaseParams param, object value) { // Engine params can't be set once the config set goes live if (this.SetParamDelegate == null) { this.ParamStore[(int)param] = value; } else { throw new EsentAlreadyInitializedException(); } } } }