/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 2009 Oracle.  All rights reserved.
 *
 */
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using BerkeleyDB.Internal;

namespace BerkeleyDB {
    /// <summary>
    /// A class representing a Berkeley DB database environment - a collection
    /// including support for some or all of caching, locking, logging and
    /// transaction subsystems, as well as databases and log files.
    /// </summary>
    public class DatabaseEnvironment {
        internal DB_ENV dbenv;
        private ErrorFeedbackDelegate errFeedbackHandler;
        private EnvironmentFeedbackDelegate feedbackHandler;
        private ThreadIsAliveDelegate isAliveHandler;
        private EventNotifyDelegate notifyHandler;
        private ReplicationTransportDelegate transportHandler;
        private SetThreadIDDelegate threadIDHandler;
        private SetThreadNameDelegate threadNameHandler;
        private string _pfx;
        private DBTCopyDelegate CopyDelegate;
        private BDB_ErrcallDelegate doErrFeedbackRef;
        private BDB_EnvFeedbackDelegate doFeedbackRef;
        private BDB_EventNotifyDelegate doNotifyRef;
        private BDB_IsAliveDelegate doIsAliveRef;
        private BDB_RepTransportDelegate doRepTransportRef;
        private BDB_ThreadIDDelegate doThreadIDRef;
        private BDB_ThreadNameDelegate doThreadNameRef;

        #region Callbacks
        private static void doNotify(IntPtr env, uint eventcode, byte[] event_info) {
            DB_ENV dbenv = new DB_ENV(env, false);
            
            dbenv.api2_internal.notifyHandler(
                (NotificationEvent)eventcode, event_info);
        }
        private static void doErrFeedback(IntPtr env, string pfx, string msg) {
            DB_ENV dbenv = new DB_ENV(env, false);
            dbenv.api2_internal.errFeedbackHandler(
                dbenv.api2_internal._pfx, msg);
        }
        private static void doFeedback(IntPtr env, int opcode, int percent) {
            DB_ENV dbenv = new DB_ENV(env, false);
            dbenv.api2_internal.feedbackHandler(
                (EnvironmentFeedbackEvent)opcode, percent);
        }
        private static int doIsAlive(IntPtr env, int pid, uint tid, uint flags) {
            DB_ENV dbenv = new DB_ENV(env, false);
            DbThreadID id = new DbThreadID(pid, tid);
            bool procOnly = (flags == DbConstants.DB_MUTEX_PROCESS_ONLY);
            return dbenv.api2_internal.isAliveHandler(id, procOnly) ? 1 : 0;
        }
        private static int doRepTransport(IntPtr envp,
            IntPtr controlp, IntPtr recp, IntPtr lsnp, int envid, uint flags) {
            DB_ENV dbenv = new DB_ENV(envp, false);
            DBT control = new DBT(controlp, false);
            DBT rec = new DBT(recp, false);
            DB_LSN tmplsn = new DB_LSN(lsnp, false);
            LSN dblsn = new LSN(tmplsn.file, tmplsn.offset);
            return dbenv.api2_internal.transportHandler(
                DatabaseEntry.fromDBT(control),
                DatabaseEntry.fromDBT(rec), dblsn, envid, flags);
        }
        private static void doThreadID(IntPtr env, IntPtr pid, IntPtr tid) {
            DB_ENV dbenv = new DB_ENV(env, false);
            DbThreadID id = dbenv.api2_internal.threadIDHandler();

            /* 
             * Sometimes the library doesn't care about either pid or tid 
             * (usually tid) and will pass NULL instead of a valid pointer.
             */
            if (pid != IntPtr.Zero)
                Marshal.WriteInt32(pid, id.processID);
            if (tid != IntPtr.Zero)
                Marshal.WriteInt32(tid, (int)id.threadID);
        }
        private static string doThreadName(IntPtr env,
            int pid, uint tid, ref string buf) {
            DB_ENV dbenv = new DB_ENV(env, false);
            DbThreadID id = new DbThreadID(pid, tid);
            string ret = 
                dbenv.api2_internal.threadNameHandler(id);
            try {
                buf = ret;
            } catch (NullReferenceException) {
                /* 
                 * The library may give us a NULL pointer in buf and there's no
                 * good way to test for that.  Just ignore the exception if
                 * we're not able to set buf.
                 */
            }
            return ret;
        }
        
        #endregion Callbacks
        private DatabaseEnvironment(uint flags) {
            dbenv = new DB_ENV(flags);
            initialize();
        }

        /* Called by Databases with private environments. */
        internal DatabaseEnvironment(DB_ENV dbenvp) {
            dbenv = dbenvp;
            initialize();
        }

        private void initialize() {
            dbenv.api2_internal = this;
            CopyDelegate = new DBTCopyDelegate(DatabaseEntry.dbt_usercopy);
            dbenv.set_usercopy(CopyDelegate);
        }

        private void Config(DatabaseEnvironmentConfig cfg) {
            //Alpha by dbenv function call
            foreach (string dirname in cfg.DataDirs)
                dbenv.add_data_dir(dirname);
            if (cfg.CreationDir != null)
                dbenv.set_create_dir(cfg.CreationDir);
            if (cfg.encryptionIsSet)
                dbenv.set_encrypt(
                    cfg.EncryptionPassword, (uint)cfg.EncryptAlgorithm);
            if (cfg.ErrorFeedback != null)
                ErrorFeedback = cfg.ErrorFeedback;
            ErrorPrefix = cfg.ErrorPrefix;
            if (cfg.EventNotify != null)
                EventNotify = cfg.EventNotify;
            if (cfg.Feedback != null)
                Feedback = cfg.Feedback;
            if (cfg.IntermediateDirMode != null)
                IntermediateDirMode = cfg.IntermediateDirMode;
            if (cfg.ThreadIsAlive != null)
                ThreadIsAlive = cfg.ThreadIsAlive;
            if (cfg.threadCntIsSet)
                ThreadCount = cfg.ThreadCount;
            if (cfg.SetThreadID != null)
                SetThreadID = cfg.SetThreadID;
            if (cfg.ThreadName != null)
                threadNameHandler = cfg.ThreadName;
            if (cfg.lckTimeoutIsSet)
                LockTimeout = cfg.LockTimeout;
            if (cfg.txnTimeoutIsSet)
                TxnTimeout = cfg.TxnTimeout;
            if (cfg.TempDir != null)
                TempDir = cfg.TempDir;
            if (cfg.maxTxnsIsSet)
                MaxTransactions = cfg.MaxTransactions;
            if (cfg.txnTimestampIsSet)
                TxnTimestamp = cfg.TxnTimestamp;
            if (cfg.Verbosity != null)
                Verbosity = cfg.Verbosity;
            if (cfg.flags != 0)
                dbenv.set_flags(cfg.flags, 1);

            if (cfg.LockSystemCfg != null) {
                if (cfg.LockSystemCfg.Conflicts != null)
                    LockConflictMatrix = cfg.LockSystemCfg.Conflicts;
                if (cfg.LockSystemCfg.DeadlockResolution != null)
                    DeadlockResolution = cfg.LockSystemCfg.DeadlockResolution;
                if (cfg.LockSystemCfg.maxLockersIsSet)
                    MaxLockers = cfg.LockSystemCfg.MaxLockers;
                if (cfg.LockSystemCfg.maxLocksIsSet)
                    MaxLocks = cfg.LockSystemCfg.MaxLocks;
                if (cfg.LockSystemCfg.maxObjectsIsSet)
                    MaxObjects = cfg.LockSystemCfg.MaxObjects;
                if (cfg.LockSystemCfg.partitionsIsSet)
                    LockPartitions = cfg.LockSystemCfg.Partitions;
            }

            if (cfg.LogSystemCfg != null) {
                if (cfg.LogSystemCfg.bsizeIsSet)
                    LogBufferSize = cfg.LogSystemCfg.BufferSize;
                if (cfg.LogSystemCfg.Dir != null)
                    LogDir = cfg.LogSystemCfg.Dir;
                if (cfg.LogSystemCfg.modeIsSet)
                    LogFileMode = cfg.LogSystemCfg.FileMode;
                if (cfg.LogSystemCfg.maxSizeIsSet)
                    MaxLogFileSize = cfg.LogSystemCfg.MaxFileSize;
                if (cfg.LogSystemCfg.regionSizeIsSet)
                    LogRegionSize = cfg.LogSystemCfg.RegionSize;
                if (cfg.LogSystemCfg.ConfigFlags != 0)
                    dbenv.log_set_config(cfg.LogSystemCfg.ConfigFlags, 1);
            }

            if (cfg.MPoolSystemCfg != null) {
                if (cfg.MPoolSystemCfg.CacheSize != null)
                    CacheSize = cfg.MPoolSystemCfg.CacheSize;
                if (cfg.MPoolSystemCfg.MaxCacheSize != null)
                    MaxCacheSize = cfg.MPoolSystemCfg.MaxCacheSize;
                if (cfg.MPoolSystemCfg.maxOpenFDIsSet)
                    MaxOpenFiles = cfg.MPoolSystemCfg.MaxOpenFiles;
                if (cfg.MPoolSystemCfg.maxSeqWriteIsSet)
                    SetMaxSequentialWrites(
                        cfg.MPoolSystemCfg.MaxSequentialWrites,
                        cfg.MPoolSystemCfg.SequentialWritePause);
                if (cfg.MPoolSystemCfg.mmapSizeSet)
                    MMapSize = cfg.MPoolSystemCfg.MMapSize;
            }

            if (cfg.MutexSystemCfg != null) {
                if (cfg.MutexSystemCfg.alignmentIsSet)
                    MutexAlignment = cfg.MutexSystemCfg.Alignment;
                /*
                 * Setting max after increment ensures that the value of max
                 * will win if both max and increment are set.  This is the
                 * behavior we document in MutexConfig.
                 */
                if (cfg.MutexSystemCfg.incrementIsSet)
                    MutexIncrement = cfg.MutexSystemCfg.Increment;
                if (cfg.MutexSystemCfg.maxIsSet)
                    MaxMutexes = cfg.MutexSystemCfg.MaxMutexes;
                if (cfg.MutexSystemCfg.numTASIsSet)
                    NumTestAndSetSpins = cfg.MutexSystemCfg.NumTestAndSetSpins;
            }

            if (cfg.RepSystemCfg != null) {
                if (cfg.RepSystemCfg.ackTimeoutIsSet)
                    RepAckTimeout = cfg.RepSystemCfg.AckTimeout;
                if (cfg.RepSystemCfg.BulkTransfer)
                    RepBulkTransfer = true;
                if (cfg.RepSystemCfg.checkpointDelayIsSet)
                    RepCheckpointDelay = cfg.RepSystemCfg.CheckpointDelay;
                if (cfg.RepSystemCfg.clockskewIsSet)
                    RepSetClockskew(cfg.RepSystemCfg.ClockskewFast,
                        cfg.RepSystemCfg.ClockskewSlow);
                if (cfg.RepSystemCfg.connectionRetryIsSet)
                    RepConnectionRetry = cfg.RepSystemCfg.ConnectionRetry;
                if (cfg.RepSystemCfg.DelayClientSync)
                    RepDelayClientSync = true;
                if (cfg.RepSystemCfg.electionRetryIsSet)
                    RepElectionRetry = cfg.RepSystemCfg.ElectionRetry;
                if (cfg.RepSystemCfg.electionTimeoutIsSet)
                    RepElectionTimeout = cfg.RepSystemCfg.ElectionTimeout;
                if (cfg.RepSystemCfg.fullElectionTimeoutIsSet)
                    RepFullElectionTimeout =
                        cfg.RepSystemCfg.FullElectionTimeout;
                if (cfg.RepSystemCfg.heartbeatMonitorIsSet)
                    RepHeartbeatMonitor = cfg.RepSystemCfg.HeartbeatMonitor;
                if (cfg.RepSystemCfg.heartbeatSendIsSet)
                    RepHeartbeatSend = cfg.RepSystemCfg.HeartbeatSend;
                if (cfg.RepSystemCfg.leaseTimeoutIsSet)
                    RepLeaseTimeout = cfg.RepSystemCfg.LeaseTimeout;
                if (cfg.RepSystemCfg.NoAutoInit)
                    RepNoAutoInit = true;
                if (cfg.RepSystemCfg.NoBlocking)
                    RepNoBlocking = true;
                if (cfg.RepSystemCfg.nsitesIsSet)
                    RepNSites = cfg.RepSystemCfg.NSites;
                if (cfg.RepSystemCfg.remoteAddrs.Count > 0)
                    foreach (ReplicationHostAddress addr
                        in cfg.RepSystemCfg.remoteAddrs.Keys)
                        RepMgrAddRemoteSite(addr,
                            cfg.RepSystemCfg.remoteAddrs[addr]);
                if (cfg.RepSystemCfg.priorityIsSet)
                    RepPriority = cfg.RepSystemCfg.Priority;
                if (cfg.RepSystemCfg.RepMgrAckPolicy != null)
                    RepMgrAckPolicy = cfg.RepSystemCfg.RepMgrAckPolicy;
                if (cfg.RepSystemCfg.retransmissionRequestIsSet)
                    RepSetRetransmissionRequest(
                        cfg.RepSystemCfg.RetransmissionRequestMin,
                        cfg.RepSystemCfg.RetransmissionRequestMax);
                if (cfg.RepSystemCfg.RepMgrLocalSite != null)
                    RepMgrLocalSite = cfg.RepSystemCfg.RepMgrLocalSite;
                if (cfg.RepSystemCfg.Strict2Site)
                    RepStrict2Site = true;
                if (cfg.RepSystemCfg.transmitLimitIsSet)
                    RepSetTransmitLimit(
                        cfg.RepSystemCfg.TransmitLimitGBytes,
                        cfg.RepSystemCfg.TransmitLimitBytes);
                if (cfg.RepSystemCfg.UseMasterLeases)
                    RepUseMasterLeases = true;
            }

        }

        #region Properties
        /// <summary>
        /// If true, database operations for which no explicit transaction
        /// handle was specified, and which modify databases in the database
        /// environment, will be automatically enclosed within a transaction.
        /// </summary>
        public bool AutoCommit {
            get {
                uint flags = 0;
                dbenv.get_flags(ref flags);
                return (flags & DbConstants.DB_AUTO_COMMIT) != 0;
            }
            set {
                dbenv.set_flags(DbConstants.DB_AUTO_COMMIT, value ? 1 : 0);
            }
        }
        /// <summary>
        /// The size of the shared memory buffer pool -- that is, the cache.
        /// </summary>
        /// <remarks>
        /// <para>
        /// The cache should be the size of the normal working data set of the
        /// application, with some small amount of additional memory for unusual
        /// situations. (Note: the working set is not the same as the number of
        /// pages accessed simultaneously, and is usually much larger.)
        /// </para>
        /// <para>
        /// The default cache size is 256KB, and may not be specified as less
        /// than 20KB. Any cache size less than 500MB is automatically increased
        /// by 25% to account for buffer pool overhead; cache sizes larger than
        /// 500MB are used as specified. The maximum size of a single cache is
        /// 4GB on 32-bit systems and 10TB on 64-bit systems. (All sizes are in
        /// powers-of-two, that is, 256KB is 2^18 not 256,000.) For information
        /// on tuning the Berkeley DB cache size, see Selecting a cache size in
        /// the Programmer's Reference Guide.
        /// </para>
        /// </remarks>
        public CacheInfo CacheSize {
            get {
                uint gb = 0;
                uint b = 0;
                int n = 0;
                dbenv.get_cachesize(ref gb, ref b, ref n);
                return new CacheInfo(gb, b, n);
            }
            set {
                if (value != null)
                dbenv.set_cachesize(
                    value.Gigabytes, value.Bytes, value.NCaches);
            }
        }
        /// <summary>
        /// If true, Berkeley DB Concurrent Data Store applications will perform
        /// locking on an environment-wide basis rather than on a per-database
        /// basis. 
        /// </summary>
        public bool CDB_ALLDB {
            get {
                uint flags = 0;
                dbenv.get_flags(ref flags);
                return (flags & DbConstants.DB_CDB_ALLDB) != 0;
            }
        }
        /// <summary>
        /// If true, Berkeley DB subsystems will create any underlying files, as
        /// necessary.
        /// </summary>
        public bool Create { 
            get {
                uint flags = 0;
                dbenv.get_open_flags(ref flags);
                return (flags & DbConstants.DB_CREATE) != 0;
            } 
        }
        /// <summary>
        /// The array of directories where database files are stored.
        /// </summary>
        public List<string> DataDirs { get { return dbenv.get_data_dirs(); } }

        /// <summary>
        /// The deadlock detector configuration, specifying what lock request(s)
        /// should be rejected. As transactions acquire locks on behalf of a
        /// single locker ID, rejecting a lock request associated with a
        /// transaction normally requires the transaction be aborted.
        /// </summary>
        public DeadlockPolicy DeadlockResolution {
            get {
                uint mode = 0;
                dbenv.get_lk_detect(ref mode);
                return DeadlockPolicy.fromPolicy(mode);
            }
            set {
                if (value != null)
                    dbenv.set_lk_detect(value.policy);
                else
                    dbenv.set_lk_detect(DeadlockPolicy.DEFAULT.policy);
            }
        }
        /// <summary>
        /// The algorithm used by the Berkeley DB library to perform encryption
        /// and decryption. 
        /// </summary>
        public EncryptionAlgorithm EncryptAlgorithm {
            get {
                uint flags = 0;
                dbenv.get_encrypt_flags(ref flags);
                return (EncryptionAlgorithm)Enum.ToObject(
                    typeof(EncryptionAlgorithm), flags);
            }
        }
        /// <summary>
        /// The mechanism for reporting detailed error messages to the
        /// application.
        /// </summary>
        /// <remarks>
        /// <para>
        /// When an error occurs in the Berkeley DB library, a
        /// <see cref="DatabaseException"/>, or subclass of DatabaseException,
        /// is thrown. In some cases, however, the exception may be insufficient
        /// to completely describe the cause of the error, especially during
        /// initial application debugging.
        /// </para>
		/// <para>
        /// In some cases, when an error occurs, Berkeley DB will call the given
        /// delegate with additional error information. It is up to the delegate
        /// to display the error message in an appropriate manner.
        /// </para>
		/// <para>
        /// Setting ErrorFeedback to NULL unconfigures the callback interface.
        /// </para>
		/// <para>
        /// This error-logging enhancement does not slow performance or
        /// significantly increase application size, and may be run during
        /// normal operation as well as during application debugging.
        /// </para>
		/// </remarks>
        public ErrorFeedbackDelegate ErrorFeedback {
            get { return errFeedbackHandler; }
            set {
                if (value == null)
                    dbenv.set_errcall(null);
                else if (errFeedbackHandler == null) {
                    if (doErrFeedbackRef == null)
                        doErrFeedbackRef = new BDB_ErrcallDelegate(doErrFeedback);
                    dbenv.set_errcall(doErrFeedbackRef);
                }
                errFeedbackHandler = value;
            }
        }
        /// <summary>
        /// The prefix string that appears before error messages issued by
        /// Berkeley DB.
        /// </summary>
        /// <remarks>
        /// <para>
        /// For databases opened inside of a DatabaseEnvironment, setting
        /// ErrorPrefix affects the entire environment and is equivalent to
        /// setting <see cref="DatabaseEnvironment.ErrorPrefix"/>.
        /// </para>
		/// <para>
        /// Setting ErrorPrefix configures operations performed using the
        /// specified object, not all operations performed on the underlying
        /// database. 
        /// </para>
        /// </remarks>
        public string ErrorPrefix {
            get { return _pfx; }
            set { _pfx = value; }
        }

        /// <summary>
        /// A delegate which is called to notify the process of specific
        /// Berkeley DB events. 
        /// </summary>
        public EventNotifyDelegate EventNotify {
            get { return notifyHandler; }
            set {
                if (value == null)
                    dbenv.set_event_notify(null);
                else if (notifyHandler == null) {
                    if (doNotifyRef == null)
                        doNotifyRef = new BDB_EventNotifyDelegate(doNotify);
                    dbenv.set_event_notify(doNotifyRef);
                }

                notifyHandler = value;
            }
        }
        /// <summary>
        /// Monitor progress within long running operations.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Some operations performed by the Berkeley DB library can take
        /// non-trivial amounts of time. The Feedback delegate can be used by
        /// applications to monitor progress within these operations. When an
        /// operation is likely to take a long time, Berkeley DB will call the
        /// specified delegate with progress information.
        /// </para>
        /// <para>
        /// It is up to the delegate to display this information in an
        /// appropriate manner. 
        /// </para>
        /// </remarks>
        public EnvironmentFeedbackDelegate Feedback {
            get { return feedbackHandler; }
            set {
                if (value == null)
                    dbenv.set_feedback(null);
                else if (feedbackHandler == null) {
                    if (doFeedbackRef == null)
                        doFeedbackRef = new BDB_EnvFeedbackDelegate(doFeedback);
                    dbenv.set_feedback(doFeedbackRef);
                }
                feedbackHandler = value;
            }
        }
        /// <summary>
        /// If true, flush database writes to the backing disk before returning
        /// from the write system call, rather than flushing database writes
        /// explicitly in a separate system call, as necessary.
        /// </summary>
        /// <remarks>
        /// This flag may result in inaccurate file modification times and other
        /// file-level information for Berkeley DB database files. This flag
        /// will almost certainly result in a performance decrease on most
        /// systems.
        /// </remarks>
        public bool ForceFlush {
            get {
                uint flags = 0;
                dbenv.get_flags(ref flags);
                return (flags & DbConstants.DB_DSYNC_DB) != 0;
            }
            set {
                dbenv.set_flags(DbConstants.DB_DSYNC_DB, value ? 1 : 0);
            }
        }
        /// <summary>
        /// If true, the object is free-threaded; that is, concurrently usable
        /// by multiple threads in the address space.
        /// </summary>
        public bool FreeThreaded { 
            get {
                uint flags = 0;
                dbenv.get_open_flags(ref flags);
                return (flags & DbConstants.DB_THREAD) != 0;
            }
        }
        /// <summary>
        /// The database environment home directory.
        /// </summary>
        public string Home {
            get {
                string dir = "";
                dbenv.get_home(ref dir);
                return dir;
            }
        }
        /// <summary>
        /// If true, Berkeley DB will page-fault shared regions into memory when
        /// initially creating or joining a Berkeley DB environment.
        /// </summary>
        /// <remarks>
        /// <para>
        /// In some applications, the expense of page-faulting the underlying
        /// shared memory regions can affect performance. (For example, if the
        /// page-fault occurs while holding a lock, other lock requests can
        /// convoy, and overall throughput may decrease.)
        /// </para>
        /// <para>
        /// In addition to page-faulting, Berkeley DB will write the shared
        /// regions when creating an environment, forcing the underlying virtual
        /// memory and filesystems to instantiate both the necessary memory and
        /// the necessary disk space. This can also avoid out-of-disk space
        /// failures later on.
        /// </para>
        /// </remarks>
        public bool InitRegions {
            get {
                uint flags = 0;
                dbenv.get_flags(ref flags);
                return (flags & DbConstants.DB_REGION_INIT) != 0;
            }
            set {
                dbenv.set_flags(DbConstants.DB_REGION_INIT, value ? 1 : 0);
            }
        }
        /// <summary>
        /// The intermediate directory permissions. 
        /// </summary>
        public string IntermediateDirMode {
            get {
                string ret = "";
                dbenv.get_intermediate_dir_mode(ref ret);
                return ret;
            }
            private set {
                dbenv.set_intermediate_dir_mode(value);
            }
        }
        
        /// <summary>
        /// The current lock conflicts array.
        /// </summary>
        public byte[,] LockConflictMatrix {
            get {
                int sz = 0;
                dbenv.get_lk_conflicts_nmodes(ref sz);
                byte [,] ret = new byte[sz, sz];
                dbenv.get_lk_conflicts(ret);
                return ret;
            }
            private set {
                // Matrix dimensions checked in LockingConfig.
                dbenv.set_lk_conflicts(value, (int)Math.Sqrt(value.Length));
            }
        }
        /// <summary>
        /// If true, lock shared Berkeley DB environment files and memory-mapped
        /// databases into memory.
        /// </summary>
        public bool Lockdown { 
            get {
                uint flags = 0;
                dbenv.get_open_flags(ref flags);
                return (flags & DbConstants.DB_LOCKDOWN) != 0;
            }
        }
        /// <summary>
        /// The number of lock table partitions used in the Berkeley DB
        /// environment.
        /// </summary>
        public uint LockPartitions {
            get {
                uint ret = 0;
                dbenv.get_lk_partitions(ref ret);
                return ret;
            }
            private set {
                dbenv.set_lk_partitions(value);
            }
        }
        /// <summary>
        /// A value, in microseconds, representing lock timeouts.
        /// </summary>
        /// <remarks>
        /// <para>
        /// All timeouts are checked whenever a thread of control blocks on a
        /// lock or when deadlock detection is performed. As timeouts are only
        /// checked when the lock request first blocks or when deadlock
        /// detection is performed, the accuracy of the timeout depends on how
        /// often deadlock detection is performed.
        /// </para>
        /// <para>
        /// Timeout values specified for the database environment may be
        /// overridden on a per-transaction basis, see
        /// <see cref="Transaction.SetLockTimeout"/>.
        /// </para>
        /// </remarks>
        public uint LockTimeout {
            get {
                uint timeout = 0;
                dbenv.get_timeout(ref timeout, DbConstants.DB_SET_LOCK_TIMEOUT);
                return timeout;
            }
            set {
                dbenv.set_timeout(value, DbConstants.DB_SET_LOCK_TIMEOUT);
            }
        }
        /// <summary>
        /// The size of the in-memory log buffer, in bytes
        /// </summary>
        public uint LogBufferSize {
            get {
                uint ret = 0;
                dbenv.get_lg_bsize(ref ret);
                return ret;
            }
            private set {
                dbenv.set_lg_bsize(value);
            }
        }
        /// <summary>
        /// The path of a directory to be used as the location of logging files.
        /// Log files created by the Log Manager subsystem will be created in
        /// this directory. 
        /// </summary>
        public string LogDir {
            get {
                string ret = "";
                dbenv.get_lg_dir(ref ret);
                return ret;
            }
            private set {
                dbenv.set_lg_dir(value);
            }
        }
        /// <summary>
        /// The absolute file mode for created log files. This property is only
        /// useful for the rare Berkeley DB application that does not control
        /// its umask value.
        /// </summary>
        /// <remarks>
        /// Normally, if Berkeley DB applications set their umask appropriately,
        /// all processes in the application suite will have read permission on
        /// the log files created by any process in the application suite.
        /// However, if the Berkeley DB application is a library, a process
        /// using the library might set its umask to a value preventing other
        /// processes in the application suite from reading the log files it
        /// creates. In this rare case, this property can be used to set the
        /// mode of created log files to an absolute value.
        /// </remarks>
        public int LogFileMode {
            get {
                int ret = 0;
                dbenv.get_lg_filemode(ref ret);
                return ret;
            }
            set {
                dbenv.set_lg_filemode(value);
            }
        }
        /// <summary>
        /// If true, system buffering is turned off for Berkeley DB log files to
        /// avoid double caching. 
        /// </summary>
        public bool LogNoBuffer {
            get {
                int onoff = 0;
                dbenv.log_get_config(DbConstants.DB_LOG_DIRECT, ref onoff);
                return (onoff != 0);
            }
            set {
                dbenv.log_set_config(DbConstants.DB_LOG_DIRECT, value ? 1 : 0);
            }
        }
        /// <summary>
        /// If true, Berkeley DB will flush log writes to the backing disk
        /// before returning from the write system call, rather than flushing
        /// log writes explicitly in a separate system call, as necessary.
        /// </summary>
        public bool LogForceSync {
            get{
                int onoff = 0;
                dbenv.log_get_config(DbConstants.DB_LOG_DSYNC, ref onoff);
                return (onoff != 0);
            }
            set {
                dbenv.log_set_config(DbConstants.DB_LOG_DSYNC, value ? 1 : 0);
            }
        }
        /// <summary>
        /// If true, Berkeley DB will automatically remove log files that are no
        /// longer needed.
        /// </summary>
        public bool LogAutoRemove {
            get {
                int onoff = 0;
                dbenv.log_get_config(DbConstants.DB_LOG_AUTO_REMOVE, ref onoff);
                return (onoff != 0);
            }
            set {
                dbenv.log_set_config(
                    DbConstants.DB_LOG_AUTO_REMOVE, value ? 1 : 0);
            }
        }
        /// <summary>
        /// If true, transaction logs are maintained in memory rather than on
        /// disk. This means that transactions exhibit the ACI (atomicity,
        /// consistency, and isolation) properties, but not D (durability).
        /// </summary>
        public bool LogInMemory {
            get {
                int onoff = 0;
                dbenv.log_get_config(DbConstants.DB_LOG_IN_MEMORY, ref onoff);
                return (onoff != 0);
            }
        }
        /// <summary>
        /// If true, all pages of a log file are zeroed when that log file is
        /// created.
        /// </summary>
        public bool LogZeroOnCreate {
            get {
                int onoff = 0;
                dbenv.log_get_config(DbConstants.DB_LOG_ZERO, ref onoff);
                return (onoff != 0);
            }
        }
        /// <summary>
        /// The size of the underlying logging area of the Berkeley DB
        /// environment, in bytes.
        /// </summary>
        public uint LogRegionSize {
            get {
                uint ret = 0;
                dbenv.get_lg_regionmax(ref ret);
                return ret;
            }
            private set {
                dbenv.set_lg_regionmax(value);
            }
        }
        /// <summary>
        /// The maximum cache size
        /// </summary>
        public CacheInfo MaxCacheSize {
            get {
                uint gb = 0;
                uint b = 0;
                dbenv.get_cache_max(ref gb, ref b);
                return new CacheInfo(gb, b, 0);
            }
            private set {
                dbenv.set_cache_max(value.Gigabytes, value.Bytes);
            }
        }
        /// <summary>
        /// The maximum size of a single file in the log, in bytes. Because
        /// <see cref="LSN.Offset">LSN Offsets</see> are unsigned four-byte
        /// values, the size may not be larger than the maximum unsigned
        /// four-byte value.
        /// </summary>
        /// <remarks>
        /// <para>
        /// When the logging subsystem is configured for on-disk logging, the
        /// default size of a log file is 10MB.
        /// </para>
        /// <para>
        /// When the logging subsystem is configured for in-memory logging, the
        /// default size of a log file is 256KB. In addition, the configured log
        /// buffer size must be larger than the log file size. (The logging
        /// subsystem divides memory configured for in-memory log records into
        /// "files", as database environments configured for in-memory log
        /// records may exchange log records with other members of a replication
        /// group, and those members may be configured to store log records
        /// on-disk.) When choosing log buffer and file sizes for in-memory
        /// logs, applications should ensure the in-memory log buffer size is
        /// large enough that no transaction will ever span the entire buffer,
        /// and avoid a state where the in-memory buffer is full and no space
        /// can be freed because a transaction that started in the first log
        /// "file" is still active.
        /// </para>
        /// <para>
        /// See Log File Limits in the Programmer's Reference Guide for more
        /// information.
        /// </para>
        /// <para>
        /// If no size is specified by the application, the size last specified
        /// for the database region will be used, or if no database region
        /// previously existed, the default will be used.
        /// </para>
        /// </remarks>
        public uint MaxLogFileSize {
            get {
                uint ret = 0;
                dbenv.get_lg_max(ref ret);
                return ret;
            }
            set {
                dbenv.set_lg_max(value);
            }
        }
        /// <summary>
        /// The maximum number of locking entities supported by the Berkeley DB
        /// environment.
        /// </summary>
        public uint MaxLockers {
            get {
                uint ret = 0;
                dbenv.get_lk_max_lockers(ref ret);
                return ret;
            }
            private set {
                dbenv.set_lk_max_lockers(value);
            }
        }
        /// <summary>
        /// The maximum number of locks supported by the Berkeley DB
        /// environment.
        /// </summary>
        public uint MaxLocks {
            get {
                uint ret = 0;
                dbenv.get_lk_max_locks(ref ret);
                return ret;
            }
            private set {
                dbenv.set_lk_max_locks(value);
            }
        }
        /// <summary>
        /// The total number of mutexes allocated
        /// </summary>
        public uint MaxMutexes {
            get {
                uint ret = 0;
                dbenv.mutex_get_max(ref ret);
                return ret;
            }
            private set {
                dbenv.mutex_set_max(value);
            }
        }
        /// <summary>
        /// The maximum number of locked objects
        /// </summary>
        public uint MaxObjects {
            get {
                uint ret = 0;
                dbenv.get_lk_max_objects(ref ret);
                return ret;
            }
            private set {
                dbenv.set_lk_max_objects(value);
            }
        }
        /// <summary>
        /// The number of file descriptors the library will open concurrently
        /// when flushing dirty pages from the cache.
        /// </summary>
        public int MaxOpenFiles {
            get {
                int ret = 0;
                dbenv.get_mp_max_openfd(ref ret);
                return ret;
            }
            set {
                dbenv.set_mp_max_openfd(value);
            }
        }
        /// <summary>
        /// The number of sequential write operations scheduled by the library
        /// when flushing dirty pages from the cache. 
        /// </summary>
        public int MaxSequentialWrites {
            get {
                int ret = 0;
                uint tmp = 0;
                dbenv.get_mp_max_write(ref ret, ref tmp);
                return ret;
            }
        }
        /// <summary>
        /// The number of active transactions supported by the environment. This
        /// value bounds the size of the memory allocated for transactions.
        /// Child transactions are counted as active until they either commit or
        /// abort.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Transactions that update multiversion databases are not freed until
        /// the last page version that the transaction created is flushed from
        /// cache. This means that applications using multi-version concurrency
        /// control may need a transaction for each page in cache, in the
        /// extreme case.
        /// </para>
        /// <para>
        /// When all of the memory available in the database environment for
        /// transactions is in use, calls to <see cref="BeginTransaction"/> will
        /// fail (until some active transactions complete). If MaxTransactions
        /// is never set, the database environment is configured to support at
        /// least 100 active transactions.
        /// </para>
        /// </remarks>
        public uint MaxTransactions {
            get {
                uint ret = 0;
                dbenv.get_tx_max(ref ret);
                return ret;
            }
            set {
                dbenv.set_tx_max(value);
            }
        }
        /// <summary>
        /// The maximum file size, in bytes, for a file to be mapped into the
        /// process address space. If no value is specified, it defaults to
        /// 10MB. 
        /// </summary>
        /// <remarks>
        /// Files that are opened read-only in the cache (and that satisfy a few
        /// other criteria) are, by default, mapped into the process address
        /// space instead of being copied into the local cache. This can result
        /// in better-than-usual performance because available virtual memory is
        /// normally much larger than the local cache, and page faults are
        /// faster than page copying on many systems. However, it can cause
        /// resource starvation in the presence of limited virtual memory, and
        /// it can result in immense process sizes in the presence of large
        /// databases.
        /// </remarks>
        public uint MMapSize {
            get {
                uint ret = 0;
                dbenv.get_mp_mmapsize(ref ret);
                return ret;
            }
            set {
                dbenv.set_mp_mmapsize(value);
            }
        }
        /// <summary>
        /// The mutex alignment, in bytes.
        /// </summary>
        public uint MutexAlignment {
            get {
                uint ret = 0;
                dbenv.mutex_get_align(ref ret);
                return ret;
            }
            private set {
                dbenv.mutex_set_align(value);
            }
        }
        /// <summary>
        /// The number of additional mutexes allocated.
        /// </summary>
        public uint MutexIncrement {
            get {
                uint ret = 0;
                dbenv.mutex_get_increment(ref ret);
                return ret;
            }
            private set {
                dbenv.mutex_set_increment(value);
            }
        }
        /// <summary>
        /// If true, turn off system buffering of Berkeley DB database files to
        /// avoid double caching. 
        /// </summary>
        public bool NoBuffer {
            get {
                uint flags = 0;
                dbenv.get_flags(ref flags);
                return (flags & DbConstants.DB_DIRECT_DB) != 0;
            }
            set {
                dbenv.set_flags(DbConstants.DB_DIRECT_DB, value ? 1 : 0);
            }
        }
        /// <summary>
        /// If true, Berkeley DB will grant all requested mutual exclusion
        /// mutexes and database locks without regard for their actual
        /// availability. This functionality should never be used for purposes
        /// other than debugging. 
        /// </summary>
        public bool NoLocking {
            get {
                uint flags = 0;
                dbenv.get_flags(ref flags);
                return (flags & DbConstants.DB_NOLOCKING) != 0;
            }
            set {
                dbenv.set_flags(DbConstants.DB_NOLOCKING, value ? 1 : 0);
            }
        }
        /// <summary>
        /// If true, Berkeley DB will copy read-only database files into the
        /// local cache instead of potentially mapping them into process memory.
        /// </summary>
        /// <seealso cref="MMapSize"/>
        public bool NoMMap {
            get {
                uint flags = 0;
                dbenv.get_flags(ref flags);
                return (flags & DbConstants.DB_NOMMAP) != 0;
            }
            set {
                dbenv.set_flags(DbConstants.DB_NOMMAP, value ? 1 : 0);
            }
        }
        /// <summary>
        /// If true, Berkeley DB will ignore any panic state in the database
        /// environment. (Database environments in a panic state normally refuse
        /// all attempts to call Berkeley DB functions, throwing 
        /// <see cref="RunRecoveryException"/>.) This functionality should never
        /// be used for purposes other than debugging.
        /// </summary>
        public bool NoPanic {
            get {
                uint flags = 0;
                dbenv.get_flags(ref flags);
                return (flags & DbConstants.DB_NOPANIC) != 0;
            }
            set {
                dbenv.set_flags(DbConstants.DB_NOPANIC, value ? 1 : 0);
            }
        }
        /// <summary>
        /// The number of times that test-and-set mutexes should spin without
        /// blocking. The value defaults to 1 on uniprocessor systems and to 50
        /// times the number of processors on multiprocessor systems. 
        /// </summary>
        public uint NumTestAndSetSpins {
            get {
                uint ret = 0;
                dbenv.mutex_get_tas_spins(ref ret);
                return ret;
            }
            set {
                dbenv.mutex_set_tas_spins(value);
            }
        }
        /// <summary>
        /// If true, overwrite files stored in encrypted formats before deleting
        /// them.
        /// </summary>
        /// <remarks>
        /// Berkeley DB overwrites files using alternating 0xff, 0x00 and 0xff
        /// byte patterns. For file overwriting to be effective, the underlying
        /// file must be stored on a fixed-block filesystem. Systems with
        /// journaling or logging filesystems will require operating system
        /// support and probably modification of the Berkeley DB sources.
        /// </remarks>
        public bool Overwrite {
            get {
                uint flags = 0;
                dbenv.get_flags(ref flags);
                return (flags & DbConstants.DB_OVERWRITE) != 0;
            }
            set {
                dbenv.set_flags(DbConstants.DB_OVERWRITE, value ? 1 : 0);
            }
        }
        /// <summary>
        /// If true, allocate region memory from the heap instead of from memory
        /// backed by the filesystem or system shared memory. 
        /// </summary>
        public bool Private { 
            get {
                uint flags = 0;
                dbenv.get_open_flags(ref flags);
                return (flags & DbConstants.DB_PRIVATE) != 0;
            }
        }
        /// <summary>
        /// If true, Berkeley DB will have checked to see if recovery needed to
        /// be performed before opening the database environment.
        /// </summary>
        public bool Register { 
            get {
                uint flags = 0;
                dbenv.get_open_flags(ref flags);
                return (flags & DbConstants.DB_REGISTER) != 0;
            }
        }
        /// <summary>
        /// The amount of time the replication manager's transport function
        /// waits to collect enough acknowledgments from replication group
        /// clients, before giving up and returning a failure indication. The
        /// default wait time is 1 second.
        /// </summary>
        public uint RepAckTimeout {
            get { return getRepTimeout(DbConstants.DB_REP_ACK_TIMEOUT); }
            set { 
                dbenv.rep_set_timeout(
                    DbConstants.DB_REP_ACK_TIMEOUT, value); 
            }
        }
        /// <summary>
        /// If true, the replication master sends groups of records to the
        /// clients in a single network transfer
        /// </summary>
        public bool RepBulkTransfer {
            get { return getRepConfig(DbConstants.DB_REP_CONF_BULK); }
            set { 
                dbenv.rep_set_config(
                    DbConstants.DB_REP_CONF_BULK, value ? 1 : 0);
            }
        }
        /// <summary>
        /// The amount of time a master site will delay between completing a
        /// checkpoint and writing a checkpoint record into the log.
        /// </summary>
        /// <remarks>
        /// This delay allows clients to complete their own checkpoints before
        /// the master requires completion of them. The default is 30 seconds.
        /// If all databases in the environment, and the environment's
        /// transaction log, are configured to reside in memory (never preserved
        /// to disk), then, although checkpoints are still necessary, the delay
        /// is not useful and should be set to 0.
        /// </remarks>
        public uint RepCheckpointDelay {
            get { return getRepTimeout(DbConstants.DB_REP_CHECKPOINT_DELAY); }
            set { 
                dbenv.rep_set_timeout(
                    DbConstants.DB_REP_CHECKPOINT_DELAY, value);
            }
        }
        /// <summary>
        /// The value, relative to <see cref="RepClockskewSlow"/>, of the
        /// fastest clock in the group of sites.
        /// </summary>
        public uint RepClockskewFast {
            get {
                uint fast = 0;
                uint slow = 0;
                dbenv.rep_get_clockskew(ref fast, ref slow);
                return fast;
            }
        }
        /// <summary>
        /// The value of the slowest clock in the group of sites.
        /// </summary>
        public uint RepClockskewSlow {
            get {
                uint fast = 0;
                uint slow = 0;
                dbenv.rep_get_clockskew(ref fast, ref slow);
                return slow;
            }
        }
        /// <summary>
        /// Set the clock skew ratio among replication group members based on
        /// the fastest and slowest measurements among the group for use with
        /// master leases.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Calling this method is optional, the default values for clock skew
        /// assume no skew. The user must also configure leases via
        /// <see cref="RepUseMasterLeases"/>. Additionally, the user must also
        /// set the master lease timeout via <see cref="RepLeaseTimeout"/> and
        /// the number of sites in the replication group via
        /// <see cref="RepNSites"/>. These settings may be configured in any
        /// order. For a description of the clock skew values, see Clock skew 
        /// in the Berkeley DB Programmer's Reference Guide. For a description
        /// of master leases, see Master leases in the Berkeley DB Programmer's
        /// Reference Guide.
        /// </para>
        /// <para>
        /// These arguments can be used to express either raw measurements of a
        /// clock timing experiment or a percentage across machines. For
        /// instance a group of sites have a 2% variance, then
        /// <paramref name="fast"/> should be set to 102, and
        /// <paramref name="slow"/> should be set to 100. Or, for a 0.03%
        /// difference, you can use 10003 and 10000 respectively.
        /// </para>
        /// </remarks>
        /// <param name="fast">
        /// The value, relative to <paramref name="slow"/>, of the fastest clock
        /// in the group of sites.
        /// </param>
        /// <param name="slow">
        /// The value of the slowest clock in the group of sites.
        /// </param>
        public void RepSetClockskew(uint fast, uint slow) {
            dbenv.rep_set_clockskew(fast, slow);
        }
        /// <summary>
        /// The amount of time the replication manager will wait before trying
        /// to re-establish a connection to another site after a communication
        /// failure. The default wait time is 30 seconds.
        /// </summary>
        public uint RepConnectionRetry {
            get { return getRepTimeout(DbConstants.DB_REP_CONNECTION_RETRY); }
            set { 
                dbenv.rep_set_timeout(
                    DbConstants.DB_REP_CONNECTION_RETRY, value);
            }
        }
        /// <summary>
        /// If true, the client should delay synchronizing to a newly declared
        /// master (defaults to false). Clients configured in this way will remain
        /// unsynchronized until the application calls <see cref="RepSync"/>. 
        /// </summary>
        public bool RepDelayClientSync {
            get { return getRepConfig(DbConstants.DB_REP_CONF_DELAYCLIENT); }
            set { 
                dbenv.rep_set_config(
                    DbConstants.DB_REP_CONF_DELAYCLIENT, value ? 1 : 0);
            }
        }
        /// <summary>
        /// Configure the amount of time the replication manager will wait
        /// before retrying a failed election. The default wait time is 10
        /// seconds. 
        /// </summary>
        public uint RepElectionRetry {
            get { return getRepTimeout(DbConstants.DB_REP_ELECTION_RETRY); }
            set { 
                dbenv.rep_set_timeout(DbConstants.DB_REP_ELECTION_RETRY, value);
            }
        }
        /// <summary>
        /// The timeout period for an election. The default timeout is 2
        /// seconds.
        /// </summary>
        public uint RepElectionTimeout {
            get { return getRepTimeout(DbConstants.DB_REP_ELECTION_TIMEOUT); }
            set { 
                dbenv.rep_set_timeout(
                    DbConstants.DB_REP_ELECTION_TIMEOUT, value);
            }
        }
        /// <summary>
        /// An optional configuration timeout period to wait for full election
        /// participation the first time the replication group finds a master.
        /// By default this option is turned off and normal election timeouts
        /// are used. (See the Elections section in the Berkeley DB Reference
        /// Guide for more information.) 
        /// </summary>
        public uint RepFullElectionTimeout {
            get { 
                return getRepTimeout(
                    DbConstants.DB_REP_FULL_ELECTION_TIMEOUT);
            }
            set { 
                dbenv.rep_set_timeout(
                    DbConstants.DB_REP_FULL_ELECTION_TIMEOUT, value);
            }
        }
        /// <summary>
        /// The amount of time the replication manager, running at a client
        /// site, waits for some message activity on the connection from the
        /// master (heartbeats or other messages) before concluding that the
        /// connection has been lost. When 0 (the default), no monitoring is
        /// performed.
        /// </summary>
        public uint RepHeartbeatMonitor {
            get { return getRepTimeout(DbConstants.DB_REP_HEARTBEAT_MONITOR); }
            set { 
                dbenv.rep_set_timeout(
                    DbConstants.DB_REP_HEARTBEAT_MONITOR, value);
            }
        }
        /// <summary>
        /// The frequency at which the replication manager, running at a master
        /// site, broadcasts a heartbeat message in an otherwise idle system.
        /// When 0 (the default), no heartbeat messages will be sent. 
        /// </summary>
        public uint RepHeartbeatSend {
            get { return getRepTimeout(DbConstants.DB_REP_HEARTBEAT_SEND); }
            set { 
                dbenv.rep_set_timeout(DbConstants.DB_REP_HEARTBEAT_SEND, value);
            }
        }
        /// <summary>
        /// The amount of time a client grants its master lease to a master.
        /// When using master leases all sites in a replication group must use
        /// the same lease timeout value. There is no default value. If leases
        /// are desired, this method must be called prior to calling
        /// <see cref="RepStartClient"/> or <see cref="RepStartMaster"/>.
        /// </summary>
        public uint RepLeaseTimeout {
            get { return getRepTimeout(DbConstants.DB_REP_LEASE_TIMEOUT); }
            set { 
                dbenv.rep_set_timeout(DbConstants.DB_REP_LEASE_TIMEOUT, value);
            }
        }
        /// <summary>
        /// Specify how master and client sites will handle acknowledgment of
        /// replication messages which are necessary for "permanent" records.
        /// The current implementation requires all sites in a replication group
        /// configure the same acknowledgement policy. 
        /// </summary>
        /// <seealso cref="RepAckTimeout"/>
        public AckPolicy RepMgrAckPolicy {
            get {
                int policy = 0;
                dbenv.repmgr_get_ack_policy(ref policy);
                return AckPolicy.fromInt(policy);
            }
            set { dbenv.repmgr_set_ack_policy(value.Policy); }
        }
        /// <summary>
        /// The host information for the local system. 
        /// </summary>
        public ReplicationHostAddress RepMgrLocalSite {
            set { dbenv.repmgr_set_local_site(value.Host, value.Port, 0); }
        }
        /// <summary>
        /// The status of the sites currently known by the replication manager. 
        /// </summary>
        public RepMgrSite[] RepMgrRemoteSites {
            get { return dbenv.repmgr_site_list(); }
        }
        /// <summary>
        /// If true, the replication master will not automatically re-initialize
        /// outdated clients (defaults to false). 
        /// </summary>
        public bool RepNoAutoInit {
            get { return getRepConfig(DbConstants.DB_REP_CONF_NOAUTOINIT); }
            set {
                dbenv.rep_set_config(
                    DbConstants.DB_REP_CONF_NOAUTOINIT, value ? 1 : 0);
            }
        }
        /// <summary>
        /// If true, Berkeley DB method calls that would normally block while
        /// clients are in recovery will return errors immediately (defaults to
        /// false).
        /// </summary>
        public bool RepNoBlocking {
            get { return getRepConfig(DbConstants.DB_REP_CONF_NOWAIT); }
            set { 
                dbenv.rep_set_config(
                    DbConstants.DB_REP_CONF_NOWAIT, value ? 1 : 0);
            }
        }
        /// <summary>
        /// The total number of sites in the replication group.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This setting is typically used by applications which use the
        /// Berkeley DB library "replication manager" support. (However, see
        /// also <see cref="RepHoldElection"/>, the description of the nsites
        /// parameter.)
        /// </para>
        /// </remarks>
        public uint RepNSites {
            get {
                uint ret = 0;
                dbenv.rep_get_nsites(ref ret);
                return ret;
            }
            set { dbenv.rep_set_nsites(value); }
        }
        /// <summary>
        /// The database environment's priority in replication group elections.
        /// A special value of 0 indicates that this environment cannot be a
        /// replication group master. If not configured, then a default value
        /// of 100 is used.
        /// </summary>
        public uint RepPriority {
            get {
                uint ret = 0;
                dbenv.rep_get_priority(ref ret);
                return ret;
            }
            set { dbenv.rep_set_priority(value); }
        }
        /// <summary>
        /// The minimum number of microseconds a client waits before requesting
        /// retransmission.
        /// </summary>
        public uint RepRetransmissionRequestMin {
            get {
                uint min = 0;
                uint max = 0;
                dbenv.rep_get_request(ref min, ref max);
                return min;
            }
        }
        /// <summary>
        /// The maximum number of microseconds a client waits before requesting
        /// retransmission.
        /// </summary>
        public uint RepRetransmissionRequestMax {
            get {
                uint min = 0;
                uint max = 0;
                dbenv.rep_get_request(ref min, ref max);
                return max;
            }
        }
        /// <summary>
        /// Set a threshold for the minimum and maximum time that a client waits
        /// before requesting retransmission of a missing message.
        /// </summary>
        /// <remarks>
        /// <para>
        /// If the client detects a gap in the sequence of incoming log records
        /// or database pages, Berkeley DB will wait for at least
        /// <paramref name="min"/> microseconds before requesting retransmission
        /// of the missing record. Berkeley DB will double that amount before
        /// requesting the same missing record again, and so on, up to a
        /// maximum threshold of <paramref name="max"/> microseconds.
        /// </para>
        /// <para>
        /// These values are thresholds only. Since Berkeley DB has no thread
        /// available in the library as a timer, the threshold is only checked
        /// when a thread enters the Berkeley DB library to process an incoming
        /// replication message. Any amount of time may have passed since the
        /// last message arrived and Berkeley DB only checks whether the amount
        /// of time since a request was made is beyond the threshold value or
        /// not.
        /// </para>
        /// <para>
        /// By default the minimum is 40000 and the maximum is 1280000 (1.28
        /// seconds). These defaults are fairly arbitrary and the application
        /// likely needs to adjust these. The values should be based on expected
        /// load and performance characteristics of the master and client host
        /// platforms and transport infrastructure as well as round-trip message
        /// time.
        /// </para></remarks>
        /// <param name="min">
        /// The minimum number of microseconds a client waits before requesting
        /// retransmission.
        /// </param>
        /// <param name="max">
        /// The maximum number of microseconds a client waits before requesting
        /// retransmission.
        /// </param>
        public void RepSetRetransmissionRequest(uint min, uint max) {
            dbenv.rep_set_request(min, max);
        }
        /// <summary>
        /// Replication Manager observes the strict "majority" rule in managing
        /// elections, even in a group with only 2 sites. This means the client
        /// in a 2-site group will be unable to take over as master if the
        /// original master fails or becomes disconnected. (See the Elections
        /// section in the Berkeley DB Reference Guide for more information.)
        /// Both sites in the replication group should have the same value for
        /// this parameter.
        /// </summary>
        public bool RepStrict2Site {
            get { 
                return getRepConfig(DbConstants.DB_REPMGR_CONF_2SITE_STRICT);
            }
            set {
                dbenv.rep_set_config(
                    DbConstants.DB_REPMGR_CONF_2SITE_STRICT, value ? 1 : 0);
            }
        }
        /// <summary>
        /// The gigabytes component of the byte-count limit on the amount of
        /// data that will be transmitted from a site in response to a single
        /// message processed by <see cref="RepProcessMessage"/>.
        /// </summary>
        public uint RepTransmitLimitGBytes {
            get {
                uint gb = 0;
                uint b = 0;
                dbenv.rep_get_limit(ref gb, ref b);
                return gb;
            }
        }
        /// <summary>
        /// The bytes component of the byte-count limit on the amount of data
        /// that will be transmitted from a site in response to a single
        /// message processed by <see cref="RepProcessMessage"/>.
        /// </summary>
        public uint RepTransmitLimitBytes {
            get {
                uint gb = 0;
                uint b = 0;
                dbenv.rep_get_limit(ref gb, ref b);
                return b;
            }
        }
        /// <summary>
        /// Set a byte-count limit on the amount of data that will be
        /// transmitted from a site in response to a single message processed by
        /// <see cref="RepProcessMessage"/>. The limit is not a hard limit, and
        /// the record that exceeds the limit is the last record to be sent. 
        /// </summary>
        /// <remarks>
        /// <para>
        /// Record transmission throttling is turned on by default with a limit
        /// of 10MB.
        /// </para>
        /// <para>
        /// If both <paramref name="GBytes"/> and <paramref name="Bytes"/> are
        /// zero, then the transmission limit is turned off.
        /// </para>
        /// </remarks>
        /// <param name="GBytes">
        /// The number of gigabytes which, when added to
        /// <paramref name="Bytes"/>, specifies the maximum number of bytes that
        /// will be sent in a single call to <see cref="RepProcessMessage"/>.
        /// </param>
        /// <param name="Bytes">
        /// The number of bytes which, when added to 
        /// <paramref name="GBytes"/>, specifies the maximum number of bytes
        /// that will be sent in a single call to
        /// <see cref="RepProcessMessage"/>.
        /// </param>
        public void RepSetTransmitLimit(uint GBytes, uint Bytes) {
            dbenv.rep_set_limit(GBytes, Bytes);
        }
        /// <summary>
        /// The delegate used to transmit data using the replication
        /// application's communication infrastructure.
        /// </summary>
        public ReplicationTransportDelegate RepTransport { 
            get { return transportHandler; }
        }
        /// <summary>
        /// Initialize the communication infrastructure for a database
        /// environment participating in a replicated application.
        /// </summary>
        /// <remarks>
        /// RepSetTransport is not called by most replication applications. It
        /// should only be called by applications implementing their own network
        /// transport layer, explicitly holding replication group elections and
        /// handling replication messages outside of the replication manager
        /// framework.
        /// </remarks>
        /// <param name="envid">
        /// The local environment's ID. It must be a non-negative integer and
        /// uniquely identify this Berkeley DB database environment (see
        /// Replication environment IDs in the Programmer's Reference Guide for
        /// more information).
        /// </param>
        /// <param name="transport">
        /// The delegate used to transmit data using the replication
        /// application's communication infrastructure.
        /// </param>
        public void RepSetTransport(int envid,
            ReplicationTransportDelegate transport) {
            if (transport == null)
                dbenv.rep_set_transport(envid, null);
            else if (transportHandler == null) {
                if (doRepTransportRef == null)
                    doRepTransportRef = new BDB_RepTransportDelegate(doRepTransport);
                dbenv.rep_set_transport(envid, doRepTransportRef);
            }                    
            transportHandler = transport;
        }
        /// <summary>
        /// If true, master leases will be used for this site (defaults to
        /// false). 
        /// </summary>
        /// <remarks>
        /// Configuring this option may result in a 
        /// <see cref="LeaseExpiredException"/> when attempting to read entries
        /// from a database after the site's master lease has expired.
        /// </remarks>
        public bool RepUseMasterLeases {
            get { return getRepConfig(DbConstants.DB_REP_CONF_LEASE); }
            set {
                dbenv.rep_set_config(
                    DbConstants.DB_REP_CONF_LEASE, value ? 1 : 0);
            }
        }
        /// <summary>
        /// If true, catastrophic recovery was run on this environment before
        /// opening it for normal use.
        /// </summary>
        public bool RunFatalRecovery {
            get {
                uint flags = 0;
                dbenv.get_open_flags(ref flags);
                return (flags & DbConstants.DB_RECOVER_FATAL) != 0;
            }
        }
        /// <summary>
        /// If true, normal recovery was run on this environment before opening
        /// it for normal use.
        /// </summary>
        public bool RunRecovery {
            get {
                uint flags = 0;
                dbenv.get_open_flags(ref flags);
                return (flags & DbConstants.DB_RECOVER) != 0;
            }
        }
        /// <summary>
        /// The number of microseconds the thread of control will pause before
        /// scheduling further write operations.
        /// </summary>
        public uint SequentialWritePause {
            get {
                int tmp = 0;
                uint ret = 0;
                dbenv.get_mp_max_write(ref tmp, ref ret);
                return ret;
            }
        }
        /// <summary>
        /// A delegate that returns a unique identifier pair for the current 
        /// thread of control.
        /// </summary>
        /// <remarks>
        /// This delegate supports <see cref="FailCheck"/>. For more
        /// information, see Architecting Data Store and Concurrent Data Store
        /// applications, and Architecting Transactional Data Store
        /// applications, both in the Berkeley DB Programmer's Reference Guide.
        /// </remarks>
        public SetThreadIDDelegate SetThreadID {
            get { return threadIDHandler; }
            set {
                if (value == null)
                    dbenv.set_thread_id(null);
                else if (threadIDHandler == null) {
                    if (doThreadIDRef == null)
                        doThreadIDRef = new BDB_ThreadIDDelegate(doThreadID);
                    dbenv.set_thread_id(doThreadIDRef);
                }
                threadIDHandler = value;
            }
        }
        /// <summary>
        /// A delegate that formats a process ID and thread ID identifier pair. 
        /// </summary>
        public SetThreadNameDelegate SetThreadName {
            get { return threadNameHandler; }
            set {
                if (value == null)
                    dbenv.set_thread_id_string(null);
                else if (threadNameHandler == null) {
                    if (doThreadNameRef == null)
                        doThreadNameRef =
                            new BDB_ThreadNameDelegate(doThreadName);
                    dbenv.set_thread_id_string(doThreadNameRef);
                }
                threadNameHandler = value;
            }
        }
        /// <summary>
        /// If true, allocate region memory from system shared memory instead of
        /// from heap memory or memory backed by the filesystem. 
        /// </summary>
        public bool SystemMemory {
            get {
                uint flags = 0;
                dbenv.get_open_flags(ref flags);
                return (flags & DbConstants.DB_SYSTEM_MEM) != 0;
            }
        }
        /// <summary>
        /// The path of a directory to be used as the location of temporary
        /// files.
        /// </summary>
        /// <remarks>
        /// <para>
        /// The files created to back in-memory access method databases will be
        /// created relative to this path. These temporary files can be quite
        /// large, depending on the size of the database.
        /// </para>
        /// <para>
        /// If no directories are specified, the following alternatives are
        /// checked in the specified order. The first existing directory path is
        /// used for all temporary files.
        /// </para>
        /// <list type="number">
        /// <item>The value of the environment variable TMPDIR.</item>
        /// <item>The value of the environment variable TEMP.</item>
        /// <item>The value of the environment variable TMP.</item>
        /// <item>The value of the environment variable TempFolder.</item>
        /// <item>The value returned by the GetTempPath interface.</item>
        /// <item>The directory /var/tmp.</item>
        /// <item>The directory /usr/tmp.</item>
        /// <item>The directory /temp.</item>
        /// <item>The directory /tmp.</item>
        /// <item>The directory C:/temp.</item>
        /// <item>The directory C:/tmp.</item>
        /// </list>
        /// <para>
        /// Environment variables are only checked if
        /// <see cref="UseEnvironmentVars"/> is true.
        /// </para>
        /// </remarks>
        public string TempDir {
            get {
                string ret = "";
                dbenv.get_tmp_dir(ref ret);
                return ret;
            }
            set { dbenv.set_tmp_dir(value); }
        }
        /// <summary>
        /// An approximate number of threads in the database environment.
        /// </summary>
        public uint ThreadCount {
            get {
                uint ret = 0;
                dbenv.get_thread_count(ref ret);
                return ret;
            }
            private set { dbenv.set_thread_count(value); }
        }
        /// <summary>
        /// A delegate that returns if a thread of control (either a true thread
        /// or a process) is still running.
        /// </summary>
        public ThreadIsAliveDelegate ThreadIsAlive {
            get { return isAliveHandler; }
            set {
                if (value == null)
                    dbenv.set_isalive(null);
                else if (isAliveHandler == null) {
                    if (doIsAliveRef == null)
                        doIsAliveRef = new BDB_IsAliveDelegate(doIsAlive);
                    dbenv.set_isalive(doIsAliveRef);
                }
                isAliveHandler = value;
            }
        }
        /// <summary>
        /// If true, database calls timing out based on lock or transaction
        /// timeout values will throw <see cref="LockNotGrantedException"/>
        /// instead of <see cref="DeadlockException"/>.
        /// </summary>
        /// <remarks>
        /// If true, this allows applications to distinguish between operations
        /// which have deadlocked and operations which have exceeded their time
        /// limits.
        /// </remarks>
        public bool TimeNotGranted {
            get {
                uint flags = 0;
                dbenv.get_flags(ref flags);
                return (flags & DbConstants.DB_TIME_NOTGRANTED) != 0;
            }
            set {
                dbenv.set_flags(DbConstants.DB_TIME_NOTGRANTED, value ? 1 : 0);
            }
        }
        /// <summary>
        /// If true, Berkeley DB will not write or synchronously flush the log
        /// on transaction commit.
        /// </summary>
        /// <remarks>
        /// This means that transactions exhibit the ACI (atomicity,
        /// consistency, and isolation) properties, but not D (durability); that
        /// is, database integrity will be maintained, but if the application or
        /// system fails, it is possible some number of the most recently
        /// committed transactions may be undone during recovery. The number of
        /// transactions at risk is governed by how many log updates can fit
        /// into the log buffer, how often the operating system flushes dirty
        /// buffers to disk, and how often the log is checkpointed.
        /// </remarks>
        public bool TxnNoSync {
            get {
                uint flags = 0;
                dbenv.get_flags(ref flags);
                return (flags & DbConstants.DB_TXN_NOSYNC) != 0;
            }
            set { dbenv.set_flags(DbConstants.DB_TXN_NOSYNC, value ? 1 : 0); }
        }
        /// <summary>
        /// If true and a lock is unavailable for any Berkeley DB operation
        /// performed in the context of a transaction, cause the operation to 
        /// throw <see cref="DeadlockException"/> (or
        /// <see cref="LockNotGrantedException"/> if configured with
        /// <see cref="TimeNotGranted"/>).
        /// </summary>
        public bool TxnNoWait {
            get {
                uint flags = 0;
                dbenv.get_flags(ref flags);
                return (flags & DbConstants.DB_TXN_NOWAIT) != 0;
            }
            set { dbenv.set_flags(DbConstants.DB_TXN_NOWAIT, value ? 1 : 0); }
        }
        /// <summary>
        /// If true, all transactions in the environment will be started as if
        /// <see cref="TransactionConfig.Snapshot"/> was passed to
        /// <see cref="BeginTransaction"/>, and all non-transactional cursors
        /// will be opened as if <see cref="CursorConfig.SnapshotIsolation"/>
        /// was passed to <see cref="BaseDatabase.Cursor"/>.
        /// </summary>
        public bool TxnSnapshot {
            get {
                uint flags = 0;
                dbenv.get_flags(ref flags);
                return (flags & DbConstants.DB_TXN_SNAPSHOT) != 0;
            }
            set { dbenv.set_flags(DbConstants.DB_TXN_SNAPSHOT, value ? 1 : 0); }
        }
        /// <summary>
        /// A value, in microseconds, representing transaction timeouts.
        /// </summary>
        /// <remarks>
        /// <para>
        /// All timeouts are checked whenever a thread of control blocks on a
        /// lock or when deadlock detection is performed. As timeouts are only
        /// checked when the lock request first blocks or when deadlock
        /// detection is performed, the accuracy of the timeout depends on how
        /// often deadlock detection is performed.
        /// </para>
        /// <para>
        /// Timeout values specified for the database environment may be
        /// overridden on a per-transaction basis, see
        /// <see cref="Transaction.SetTxnTimeout"/>.
        /// </para>
        /// </remarks>
        public uint TxnTimeout {
            get {
                uint timeout = 0;
                dbenv.get_timeout(ref timeout, DbConstants.DB_SET_TXN_TIMEOUT);
                return timeout;
            }
            set {
                dbenv.set_timeout(value, DbConstants.DB_SET_TXN_TIMEOUT);
            }
        }
        /// <summary>
        /// The recovery timestamp
        /// </summary>
        public DateTime TxnTimestamp {
            get {
                long secs = 0;
                dbenv.get_tx_timestamp(ref secs);
                DateTime epoch = new DateTime(1970, 1, 1);
                DateTime ret = epoch.AddSeconds(secs);
                return ret;
            }
            private set {
                if (value != null) {
                    TimeSpan ts = value - new DateTime(1970, 1, 1);
                    long secs = (long)ts.TotalSeconds;
                    dbenv.set_tx_timestamp(ref secs);
                }
            }
        }
        /// <summary>
        /// If true, Berkeley DB will write, but will not synchronously flush,
        /// the log on transaction commit. 
        /// </summary>
        /// <remarks>
        /// This means that transactions exhibit the ACI (atomicity,
        /// consistency, and isolation) properties, but not D (durability); that
        /// is, database integrity will be maintained, but if the system fails, 
        /// it is possible some number of the most recently committed
        /// transactions may be undone during recovery. The number of
        /// transactions at risk is governed by how often the system flushes
        /// dirty buffers to disk and how often the log is checkpointed.
        /// </remarks>
        public bool TxnWriteNoSync {
            get {
                uint flags = 0;
                dbenv.get_flags(ref flags);
                return (flags & DbConstants.DB_TXN_WRITE_NOSYNC) != 0;
            }
            set {
                dbenv.set_flags(DbConstants.DB_TXN_WRITE_NOSYNC, value ? 1 : 0);
            }
        }
        /// <summary>
        /// If true, all databases in the environment will be opened as if
        /// <see cref="DatabaseConfig.UseMVCC"/> was set.
        /// </summary>
        /// <remarks>
        /// This flag will be ignored for queue databases for which MVCC is not
        /// supported.
        /// </remarks>
        public bool UseMVCC {
            get {
                uint flags = 0;
                dbenv.get_flags(ref flags);
                return (flags & DbConstants.DB_MULTIVERSION) != 0;
            }
            set { dbenv.set_flags(DbConstants.DB_MULTIVERSION, value ? 1 : 0); }
        }
        /// <summary>
        /// If true, locking for the Berkeley DB Concurrent Data Store product
        /// was initialized.
        /// </summary>
        public bool UsingCDB {
            get {
                uint flags = 0;
                dbenv.get_open_flags(ref flags);
                return (flags & DbConstants.DB_INIT_CDB) != 0;
            }
        }
        /// <summary>
        /// If true, the locking subsystem was initialized.
        /// </summary>
        public bool UsingLocking {
            get {
                uint flags = 0;
                dbenv.get_open_flags(ref flags);
                return (flags & DbConstants.DB_INIT_LOCK) != 0;
            }
        }
        /// <summary>
        /// If true, the logging subsystem was initialized.
        /// </summary>
        public bool UsingLogging {
            get {
                uint flags = 0;
                dbenv.get_open_flags(ref flags);
                return (flags & DbConstants.DB_INIT_LOG) != 0;
            }
        }
        /// <summary>
        /// If true, the shared memory buffer pool subsystem was initialized.
        /// </summary>
        public bool UsingMPool {
            get {
                uint flags = 0;
                dbenv.get_open_flags(ref flags);
                return (flags & DbConstants.DB_INIT_MPOOL) != 0;
            }
        }
        /// <summary>
        /// If true, the replication subsystem was initialized.
        /// </summary>
        public bool UsingReplication {
            get {
                uint flags = 0;
                dbenv.get_open_flags(ref flags);
                return (flags & DbConstants.DB_INIT_REP) != 0;
            }
        }
        /// <summary>
        /// If true, the transaction subsystem was initialized.
        /// </summary>
        public bool UsingTxns {
            get {
                uint flags = 0;
                dbenv.get_open_flags(ref flags);
                return (flags & DbConstants.DB_INIT_TXN) != 0;
            }
        }
        /// <summary>
        /// Specific additional informational and debugging messages in the
        /// Berkeley DB message output.
        /// </summary>
        public VerboseMessages Verbosity {
            get {
                uint flags = 0;
                dbenv.get_verbose(ref flags);
                return VerboseMessages.FromFlags(flags);
            }
            set {
                if (value.MessagesOff != 0)
                    dbenv.set_verbose(value.MessagesOff, 0);
                if (value.MessagesOn != 0)
                    dbenv.set_verbose(value.MessagesOn, 1);
            }
        }
        /// <summary>
        /// If true, Berkeley DB will yield the processor immediately after each
        /// page or mutex acquisition.
        /// </summary>
        /// <remarks>
        /// This functionality should never be used for purposes other than
        /// stress testing.
        /// </remarks>
        public bool YieldCPU {
            get {
                uint flags = 0;
                dbenv.get_flags(ref flags);
                return (flags & DbConstants.DB_YIELDCPU) != 0;
            }
            set {
                dbenv.set_flags(DbConstants.DB_YIELDCPU, value ? 1 : 0);
            }
        }
        #endregion Properties

        /// <summary>
        /// Instantiate a new DatabaseEnvironment object and open the Berkeley
        /// DB environment represented by <paramref name="home"/>.
        /// </summary>
        /// <param name="home">
        /// The database environment's home directory.  For more information on
        /// home, and filename resolution in general, see Berkeley DB File
        /// Naming in the Programmer's Reference Guide.
        /// </param>
        /// <param name="cfg">The environment's configuration</param>
        /// <returns>A new, open DatabaseEnvironment object</returns>
        public static DatabaseEnvironment Open(
            String home, DatabaseEnvironmentConfig cfg) {
            DatabaseEnvironment env = new DatabaseEnvironment(0);
            env.Config(cfg);
            env.dbenv.open(home, cfg.openFlags, 0);
            return env;
        }

        /// <summary>
        /// Destroy a Berkeley DB environment if it is not currently in use.
        /// </summary>
        /// <overloads>
        /// <para>
        /// The environment regions, including any backing files, are removed.
        /// Any log or database files and the environment directory are not
        /// removed.
        /// </para>
        /// <para>
        /// If there are processes that have called <see cref="Open"/> without
        /// calling <see cref="Close"/> (that is, there are processes currently
        /// using the environment), Remove will fail without further action.
        /// </para>
        /// <para>
        /// Calling Remove should not be necessary for most applications because
        /// the Berkeley DB environment is cleaned up as part of normal database
        /// recovery procedures. However, applications may want to call Remove
        /// as part of application shut down to free up system resources. For
        /// example, if <see cref="DatabaseEnvironmentConfig.SystemMemory"/> was
        /// specified to <see cref="Open"/>, it may be useful to call Remove in
        /// order to release system shared memory segments that have been
        /// allocated. Or, on architectures in which mutexes require allocation
        /// of underlying system resources, it may be useful to call Remove in
        /// order to release those resources. Alternatively, if recovery is not
        /// required because no database state is maintained across failures,
        /// and no system resources need to be released, it is possible to clean
        /// up an environment by simply removing all the Berkeley DB files in
        /// the database environment's directories.
        /// </para>
        /// <para>
        /// In multithreaded applications, only a single thread may call Remove.
        /// </para>
        /// </overloads>
        /// <param name="db_home">
        /// The database environment to be removed.
        /// </param>
        public static void Remove(string db_home) {
            Remove(db_home, false, false, false);
        }
        /// <summary>
        /// Destroy a Berkeley DB environment if it is not currently in use.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Generally, <paramref name="force"/> is specified only when
        /// applications were unable to shut down cleanly, and there is a risk
        /// that an application may have died holding a Berkeley DB lock.)
        /// </para>
        /// <para>
        /// The result of attempting to forcibly destroy the environment when it
        /// is in use is unspecified. Processes using an environment often
        /// maintain open file descriptors for shared regions within it. On UNIX
        /// systems, the environment removal will usually succeed, and processes
        /// that have already joined the region will continue to run in that
        /// region without change. However, processes attempting to join the
        /// environment will either fail or create new regions. On other systems
        /// in which the unlink(2) system call will fail if any process has an
        /// open file descriptor for the file (for example Windows/NT), the
        /// region removal will fail.
        /// </para>
        /// </remarks>
        /// <param name="db_home">
        /// The database environment to be removed.
        /// </param>
        /// <param name="force">
        /// If true, the environment is removed, regardless of any processes
        /// that may still using it, and no locks are acquired during this
        /// process.
        /// </param>
        public static void Remove(string db_home, bool force) {
            Remove(db_home, force, false, false);
        }
        private static void Remove(string db_home,
            bool force, bool USE_ENVIRON, bool USE_ENVIRON_ROOT) {
            DatabaseEnvironment env = new DatabaseEnvironment(0);
            uint flags = 0;

            flags |= force ? DbConstants.DB_FORCE : 0;
            flags |= USE_ENVIRON ? DbConstants.DB_USE_ENVIRON : 0;
            flags |= USE_ENVIRON_ROOT ? DbConstants.DB_USE_ENVIRON_ROOT : 0;
            env.dbenv.remove(db_home, flags);
        }

        /// <summary>
        /// Hold an election for the master of a replication group.
        /// </summary>
        public void RepHoldElection() {
            RepHoldElection(0, 0);
        }
        /// <summary>
        /// Hold an election for the master of a replication group.
        /// </summary>
        /// <param name="nsites">
        /// The number of replication sites expected to participate in the
        /// election. Once the current site has election information from that
        /// many sites, it will short-circuit the election and immediately cast
        /// its vote for a new master. This parameter must be no less than
        /// <paramref name="nvotes"/>, or 0 if the election should use
        /// <see cref="RepNSites"/>. If an application is using master leases,
        /// then the value must be 0 and <see cref="RepNSites"/> must be used.
        /// </param>
        public void RepHoldElection(uint nsites) {
            RepHoldElection(nsites, 0);
        }
        /// <summary>
        /// Hold an election for the master of a replication group.
        /// </summary>
        /// <overloads>
        /// <para>
        /// RepHoldElection is not called by most replication applications. It
        /// should only be called by applications implementing their own network
        /// transport layer, explicitly holding replication group elections and
        /// handling replication messages outside of the replication manager
        /// framework.
        /// </para>
        /// <para>
        /// If the election is successful, Berkeley DB will notify the
        /// application of the results of the election by means of either the 
        /// <see cref="NotificationEvent.REP_ELECTED"/> or 
        /// <see cref="NotificationEvent.REP_NEWMASTER"/> events (see 
        /// <see cref="EventNotify"/>for more information). The application is
        /// responsible for adjusting its relationship to the other database
        /// environments in the replication group, including directing all
        /// database updates to the newly selected master, in accordance with
        /// the results of the election.
        /// </para>
        /// <para>
        /// The thread of control that calls RepHoldElection must not be the
        /// thread of control that processes incoming messages; processing the
        /// incoming messages is necessary to successfully complete an election.
        /// </para>
        /// <para>
        /// Before calling this method, the <see cref="RepTransport"/> delegate 
        /// must already have been configured to send replication messages.
        /// </para>
        /// </overloads>
        /// <param name="nsites">
        /// The number of replication sites expected to participate in the
        /// election. Once the current site has election information from that
        /// many sites, it will short-circuit the election and immediately cast
        /// its vote for a new master. This parameter must be no less than
        /// <paramref name="nvotes"/>, or 0 if the election should use
        /// <see cref="RepNSites"/>. If an application is using master leases,
        /// then the value must be 0 and <see cref="RepNSites"/> must be used.
        /// </param>
        /// <param name="nvotes">
        /// The minimum number of replication sites from which the current site
        /// must have election information, before the current site will cast a
        /// vote for a new master. This parameter must be no greater than
        /// <paramref name="nsites"/>, or 0 if the election should use the value
        /// ((<paramref name="nsites"/> / 2) + 1).
        /// </param>
        public void RepHoldElection(uint nsites, uint nvotes) {
            dbenv.rep_elect(nsites, nvotes, 0);
        }

        /// <summary>
        /// Add a new replication site to the replication manager's list of
        /// known sites. It is not necessary for all sites in a replication
        /// group to know about all other sites in the group. 
        /// </summary>
        /// <param name="Host">The remote site's address</param>
        /// <returns>The environment ID assigned to the remote site</returns>
        public int RepMgrAddRemoteSite(ReplicationHostAddress Host) {
            return RepMgrAddRemoteSite(Host, false);
        }

        /// <summary>
        /// Add a new replication site to the replication manager's list of
        /// known sites. It is not necessary for all sites in a replication
        /// group to know about all other sites in the group. 
        /// </summary>
        /// <remarks>
        /// Currently, the replication manager framework only supports a single
        /// client peer, and the last specified peer is used.
        /// </remarks>
        /// <param name="Host">The remote site's address</param>
        /// <param name="isPeer">
        /// If true, configure client-to-client synchronization with the
        /// specified remote site.
        /// </param>
        /// <returns>The environment ID assigned to the remote site</returns>
        public int RepMgrAddRemoteSite(
            ReplicationHostAddress Host, bool isPeer) {
            int eidp = 0;
            dbenv.repmgr_add_remote_site(Host.Host,
                Host.Port, ref eidp, isPeer ? DbConstants.DB_REPMGR_PEER : 0);
            return eidp;
        }
        
        /// <summary>
        /// Start the replication manager as a client site, and do not call for
        /// an election.
        /// </summary>
        /// <overloads>
        /// <para>
        /// There are two ways to build Berkeley DB replication applications:
        /// the most common approach is to use the Berkeley DB library
        /// "replication manager" support, where the Berkeley DB library manages
        /// the replication group, including network transport, all replication
        /// message processing and acknowledgment, and group elections.
        /// Applications using the replication manager support generally make
        /// the following calls:
        /// </para>
        /// <list type="number">
        /// <item>
        /// Configure the local site in the replication group,
        /// <see cref="RepMgrLocalSite"/>.
        /// </item>
        /// <item>
        /// Call <see cref="RepMgrAddRemoteSite"/> to configure the remote
        /// site(s) in the replication group.
        /// </item>
        /// <item>Configure the message acknowledgment policy
        /// (<see cref="RepMgrAckPolicy"/>) which provides the replication group's
        /// transactional needs.
        /// </item>
        /// <item>
        /// Configure the local site's election priority,
        /// <see cref="RepPriority"/>.
        /// </item>
        /// <item>
        /// Call <see cref="RepMgrStartClient"/> or
        /// <see cref="RepMgrStartMaster"/> to start the replication
        /// application.
        /// </item>
        /// </list>
        /// <para>
        /// For more information on building replication manager applications,
        /// please see the Replication Getting Started Guide included in the
        /// Berkeley DB documentation.
        /// </para>
        /// <para>
        /// Applications with special needs (for example, applications using
        /// network protocols not supported by the Berkeley DB replication
        /// manager), must perform additional configuration and call other
        /// Berkeley DB replication methods. For more information on building
        /// advanced replication applications, please see the Base Replication
        /// API section in the Berkeley DB Programmer's Reference Guide for more
        /// information.
        /// </para>
        /// <para>
        /// Starting the replication manager consists of opening the TCP/IP
        /// listening socket to accept incoming connections, and starting all
        /// necessary background threads. When multiple processes share a
        /// database environment, only one process can open the listening
        /// socket; <see cref="RepMgrStartClient"/> (and
        /// <see cref="RepMgrStartMaster"/>) automatically open the socket in
        /// the first process to call it, and skips this step in the later calls
        /// from other processes.
        /// </para>
        /// </overloads>
        /// <param name="nthreads">
        /// Specify the number of threads of control created and dedicated to
        /// processing replication messages. In addition to these message
        /// processing threads, the replication manager creates and manages a
        /// few of its own threads of control.
        /// </param>
        public void RepMgrStartClient(int nthreads) {
            RepMgrStartClient(nthreads, false);
        }
        /// <summary>
        /// Start the replication manager as a client site, and optionally call
        /// for an election.
        /// </summary>
        /// <param name="nthreads">
        /// Specify the number of threads of control created and dedicated to
        /// processing replication messages. In addition to these message
        /// processing threads, the replication manager creates and manages a
        /// few of its own threads of control.
        /// </param>
        /// <param name="holdElection">
        /// If true, start as a client, and call for an election if no master is
        /// found.
        /// </param>
        public void RepMgrStartClient(int nthreads, bool holdElection) {
            dbenv.repmgr_start(nthreads,
                holdElection ?
                DbConstants.DB_REP_ELECTION : DbConstants.DB_REP_CLIENT);
        }
        /// <summary>
        /// Start the replication manager as a master site, and do not call for
        /// an election.
        /// </summary>
        /// <remarks>
        /// <para>
        /// There are two ways to build Berkeley DB replication applications:
        /// the most common approach is to use the Berkeley DB library
        /// "replication manager" support, where the Berkeley DB library manages
        /// the replication group, including network transport, all replication
        /// message processing and acknowledgment, and group elections.
        /// Applications using the replication manager support generally make
        /// the following calls:
        /// </para>
        /// <list type="number">
        /// <item>
        /// Configure the local site in the replication group,
        /// <see cref="RepMgrLocalSite"/>.
        /// </item>
        /// <item>
        /// Call <see cref="RepMgrAddRemoteSite"/> to configure the remote
        /// site(s) in the replication group.
        /// </item>
        /// <item>Configure the message acknowledgment policy
        /// (<see cref="RepMgrAckPolicy"/>) which provides the replication group's
        /// transactional needs.
        /// </item>
        /// <item>
        /// Configure the local site's election priority,
        /// <see cref="RepPriority"/>.
        /// </item>
        /// <item>
        /// Call <see cref="RepMgrStartClient"/> or
        /// <see cref="RepMgrStartMaster"/> to start the replication
        /// application.
        /// </item>
        /// </list>
        /// <para>
        /// For more information on building replication manager applications,
        /// please see the Replication Getting Started Guide included in the
        /// Berkeley DB documentation.
        /// </para>
        /// <para>
        /// Applications with special needs (for example, applications using
        /// network protocols not supported by the Berkeley DB replication
        /// manager), must perform additional configuration and call other
        /// Berkeley DB replication methods. For more information on building
        /// advanced replication applications, please see the Base Replication
        /// API section in the Berkeley DB Programmer's Reference Guide for more
        /// information.
        /// </para>
        /// <para>
        /// Starting the replication manager consists of opening the TCP/IP
        /// listening socket to accept incoming connections, and starting all
        /// necessary background threads. When multiple processes share a
        /// database environment, only one process can open the listening
        /// socket; <see cref="RepMgrStartMaster"/> (and
        /// <see cref="RepMgrStartClient"/>) automatically open the socket in
        /// the first process to call it, and skips this step in the later calls
        /// from other processes.
        /// </para>
        /// </remarks>
        /// <param name="nthreads">
        /// Specify the number of threads of control created and dedicated to
        /// processing replication messages. In addition to these message
        /// processing threads, the replication manager creates and manages a
        /// few of its own threads of control.
        /// </param>
        public void RepMgrStartMaster(int nthreads) {
            dbenv.repmgr_start(nthreads, DbConstants.DB_REP_MASTER);
        }
        
        /// <summary>
        /// Process an incoming replication message sent by a member of the
        /// replication group to the local database environment. 
        /// </summary>
        /// <remarks>
        /// <para>
        /// RepProcessMessage is not called by most replication applications. It
        /// should only be called by applications implementing their own network
        /// transport layer, explicitly holding replication group elections and
        /// handling replication messages outside of the replication manager
        /// framework.
        /// </para>
        /// <para>
        /// For implementation reasons, all incoming replication messages must
        /// be processed using the same <see cref="DatabaseEnvironment"/>
        /// object. It is not required that a single thread of control process
        /// all messages, only that all threads of control processing messages
        /// use the same object.
        /// </para>
        /// <para>
        /// Before calling this method, the <see cref="RepTransport"/> delegate 
        /// must already have been configured to send replication messages.
        /// </para>
        /// </remarks>
        /// <param name="control">
        /// A copy of the control parameter specified by Berkeley DB on the
        /// sending environment.
        /// </param>
        /// <param name="rec">
        /// A copy of the rec parameter specified by Berkeley DB on the sending
        /// environment.
        /// </param>
        /// <param name="envid">
        /// The local identifier that corresponds to the environment that sent
        /// the message to be processed (see Replication environment IDs in the
        /// Programmer's Reference Guide for more information)..
        /// </param>
        /// <returns>The result of processing a message</returns>
        public RepProcMsgResult RepProcessMessage(
            DatabaseEntry control, DatabaseEntry rec, int envid) {
            DB_LSN dblsn = new DB_LSN();
            int ret = dbenv.rep_process_message(control,
                rec, envid, dblsn);
            LSN lsnp = new LSN(dblsn.file, dblsn.offset);
            RepProcMsgResult result = new RepProcMsgResult(ret, lsnp);
            if (result.Result == RepProcMsgResult.ProcMsgResult.ERROR)
                DatabaseException.ThrowException(ret);
            return result;
        }
        
        /// <summary>
        /// Configure the database environment as a client in a group of
        /// replicated database environments.
        /// </summary>
        public void RepStartClient() {
            RepStartClient(null);
        }
        /// <summary>
        /// Configure the database environment as a client in a group of
        /// replicated database environments.
        /// </summary>
        /// <overloads>
        /// <para>
        /// RepStartClient is not called by most replication applications. It
        /// should only be called by applications implementing their own network
        /// transport layer, explicitly holding replication group elections and
        /// handling replication messages outside of the replication manager
        /// framework.
        /// </para>
        /// <para>
        /// Replication master environments are the only database environments
        /// where replicated databases may be modified. Replication client
        /// environments are read-only as long as they are clients. Replication
        /// client environments may be upgraded to be replication master
        /// environments in the case that the current master fails or there is
        /// no master present. If master leases are in use, this method cannot
        /// be used to appoint a master, and should only be used to configure a
        /// database environment as a master as the result of an election.
        /// </para>
        /// <para>
        /// Before calling this method, the <see cref="RepTransport"/> delegate 
        /// must already have been configured to send replication messages.
        /// </para>
        /// </overloads>
        /// <param name="cdata">
        /// An opaque data item that is sent over the communication
        /// infrastructure when the client comes online (see Connecting to a new
        /// site in the Programmer's Reference Guide for more information). If
        /// no such information is useful, cdata should be null.
        /// </param>
        public void RepStartClient(DatabaseEntry cdata) {
            dbenv.rep_start(
                cdata, DbConstants.DB_REP_CLIENT);
        }
        /// <summary>
        /// Configure the database environment as a master in a group of
        /// replicated database environments.
        /// </summary>
        public void RepStartMaster() {
            RepStartMaster(null);
        }
        /// <summary>
        /// Configure the database environment as a master in a group of
        /// replicated database environments.
        /// </summary>
        /// <overloads>
        /// <para>
        /// RepStartMaster is not called by most replication applications. It
        /// should only be called by applications implementing their own network
        /// transport layer, explicitly holding replication group elections and
        /// handling replication messages outside of the replication manager
        /// framework.
        /// </para>
        /// <para>
        /// Replication master environments are the only database environments
        /// where replicated databases may be modified. Replication client
        /// environments are read-only as long as they are clients. Replication
        /// client environments may be upgraded to be replication master
        /// environments in the case that the current master fails or there is
        /// no master present. If master leases are in use, this method cannot
        /// be used to appoint a master, and should only be used to configure a
        /// database environment as a master as the result of an election.
        /// </para>
        /// <para>
        /// Before calling this method, the <see cref="RepTransport"/> delegate 
        /// must already have been configured to send replication messages.
        /// </para>
        /// </overloads>
        /// <param name="cdata">
        /// An opaque data item that is sent over the communication
        /// infrastructure when the client comes online (see Connecting to a new
        /// site in the Programmer's Reference Guide for more information). If
        /// no such information is useful, cdata should be null.
        /// </param>
        public void RepStartMaster(DatabaseEntry cdata) {
            dbenv.rep_start(
                cdata, DbConstants.DB_REP_MASTER);
        }

        /// <summary>
        /// Force master synchronization to begin for this client.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This method is the other half of setting
        /// <see cref="RepDelayClientSync"/>.
        /// </para>
        /// <para>
        /// If an application has configured delayed master synchronization, the
        /// application must synchronize explicitly (otherwise the client will
        /// remain out-of-date and will ignore all database changes forwarded
        /// from the replication group master). RepSync may be called any time
        /// after the client application learns that the new master has been
        /// established (by receiving
        /// <see cref="NotificationEvent.REP_NEWMASTER"/>).
        /// </para>
        /// <para>
        /// Before calling this method, the <see cref="RepTransport"/> delegate 
        /// must already have been configured to send replication messages.
        /// </para>
        /// </remarks>
        public void RepSync() {
            dbenv.rep_sync(0);
        }

        /// <summary>
        /// The names of all of the log files that are no longer in use (for
        /// example, that are no longer involved in active transactions), and
        /// that may safely be archived for catastrophic recovery and then
        /// removed from the system.
        /// </summary>
        /// <remarks>
        /// <para>
        /// The Berkeley DB interfaces to the database environment logging
        /// subsystem (for example, <see cref="Transaction.Abort"/>) may
        /// allocate log cursors and have open file descriptors for log files
        /// as well. On operating systems where filesystem related system calls
        /// (for example, rename and unlink on Windows/NT) can fail if a process
        /// has an open file descriptor for the affected file, attempting to
        /// move or remove the log files listed by ArchivableLogFiles may fail.
        /// All Berkeley DB internal use of log cursors operates on active log
        /// files only and furthermore, is short-lived in nature. So, an
        /// application seeing such a failure should be restructured to retry
        /// the operation until it succeeds. (Although this is not likely to be
        /// necessary; it is hard to imagine a reason to move or rename a log
        /// file in which transactions are being logged or aborted.)
        /// </para>
        /// <para>
        /// See the db_archive utility for more information on database archival
        /// procedures.
        /// </para>
        /// </remarks>
        /// <param name="AbsolutePaths">
        /// If true, all pathnames are returned as absolute pathnames, instead 
        /// of relative to the database home directory.
        /// </param>
        /// <returns>
        /// The names of all of the log files that are no longer in use
        /// </returns>
        public List<string> ArchivableLogFiles(bool AbsolutePaths) {
            uint flags = 0;
            flags |= AbsolutePaths ? DbConstants.DB_ARCH_ABS : 0;
            return dbenv.log_archive(flags);
        }
        /// <summary>
        /// The database files that need to be archived in order to recover the
        /// database from catastrophic failure. If any of the database files
        /// have not been accessed during the lifetime of the current log files,
        /// they will not included in this list. It is also possible that some
        /// of the files referred to by the log have since been deleted from the
        /// system. 
        /// </summary>
        /// <remarks>
        /// <para>
        /// See the db_archive utility for more information on database archival
        /// procedures.
        /// </para>
        /// </remarks>
        /// <param name="AbsolutePaths">
        /// If true, all pathnames are returned as absolute pathnames, instead 
        /// of relative to the database home directory.
        /// </param>
        /// <returns>
        /// The database files that need to be archived in order to recover the
        /// database from catastrophic failure.
        /// </returns>
        public List<string> ArchivableDatabaseFiles(bool AbsolutePaths) {
            uint flags = DbConstants.DB_ARCH_DATA;
            flags |= AbsolutePaths ? DbConstants.DB_ARCH_ABS : 0;
            return dbenv.log_archive(flags);
        }
        /// <summary>
        /// The names of all of the log files
        /// </summary>
        /// <remarks>
        /// <para>
        /// The Berkeley DB interfaces to the database environment logging
        /// subsystem (for example, <see cref="Transaction.Abort"/>) may
        /// allocate log cursors and have open file descriptors for log files
        /// as well. On operating systems where filesystem related system calls
        /// (for example, rename and unlink on Windows/NT) can fail if a process
        /// has an open file descriptor for the affected file, attempting to
        /// move or remove the log files listed by LogFiles may fail. All
        /// Berkeley DB internal use of log cursors operates on active log files
        /// only and furthermore, is short-lived in nature. So, an application
        /// seeing such a failure should be restructured to retry the operation
        /// until it succeeds. (Although this is not likely to be necessary; it
        /// is hard to imagine a reason to move or rename a log file in which
        /// transactions are being logged or aborted.)
        /// </para>
        /// <para>
        /// See the db_archive utility for more information on database archival
        /// procedures.
        /// </para>
        /// </remarks>
        /// <param name="AbsolutePaths">
        /// If true, all pathnames are returned as absolute pathnames, instead 
        /// of relative to the database home directory.
        /// </param>
        /// <returns>
        /// All the log filenames, regardless of whether or not they are in use.
        /// </returns>
        public List<string> LogFiles(bool AbsolutePaths) {
            uint flags = DbConstants.DB_ARCH_LOG;
            flags |= AbsolutePaths ? DbConstants.DB_ARCH_ABS : 0;
            return dbenv.log_archive(flags);
        }
        /// <summary>
        /// Remove log files that are no longer needed. Automatic log file
        /// removal is likely to make catastrophic recovery impossible. 
        /// </summary>
        public void RemoveUnusedLogFiles() {
            dbenv.log_archive(DbConstants.DB_ARCH_REMOVE);
        }

        /// <summary>
        /// Allocate a locker ID in an environment configured for Berkeley DB
        /// Concurrent Data Store applications.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Calling <see cref="Transaction.Commit"/> will discard the allocated
        /// locker ID.
        /// </para>
        /// <para>
        /// See Berkeley DB Concurrent Data Store applications in the
        /// Programmer's Reference Guide for more information about when this is
        /// required.
        /// </para>
        /// </remarks>
        /// <returns>
        /// A Transaction object that uniquely identifies the locker ID
        /// </returns>
        public Transaction BeginCDSGroup() {
            return new Transaction(dbenv.cdsgroup_begin());
        }

        /// <summary>
        /// Create a new transaction in the environment, with the default 
        /// configuration.
        /// </summary>
        /// <returns>A new transaction object</returns>
        public Transaction BeginTransaction() {
            return BeginTransaction(new TransactionConfig(), null);
        }
        /// <summary>
        /// Create a new transaction in the environment.
        /// </summary>
        /// <param name="cfg">
        /// The configuration properties for the transaction
        /// </param>
        /// <returns>A new transaction object</returns>
        public Transaction BeginTransaction(TransactionConfig cfg) {
            return BeginTransaction(cfg, null);
        }
        /// <summary>
        /// Create a new transaction in the environment.
        /// </summary>
        /// <remarks>
        /// In the presence of distributed transactions and two-phase commit,
        /// only the parental transaction, that is a transaction without a
        /// parent specified, should be passed as an parameter to 
        /// <see cref="Transaction.Prepare"/>.
        /// </remarks>
        /// <param name="cfg">
        /// The configuration properties for the transaction
        /// </param>
        /// <param name="parent">
        /// If the non-null, the new transaction will be a nested transaction,
        /// with <paramref name="parent"/> as the new transaction's parent.
        /// Transactions may be nested to any level.
        /// </param>
        /// <returns>A new transaction object</returns>
        public Transaction BeginTransaction(
            TransactionConfig cfg, Transaction parent) {
            DB_TXN dbtxn = dbenv.txn_begin(
                Transaction.getDB_TXN(parent), cfg.flags);
            Transaction txn = new Transaction(dbtxn);
            if (cfg.lockTimeoutIsSet)
                txn.SetLockTimeout(cfg.LockTimeout);
            if (cfg.nameIsSet)
                txn.Name = cfg.Name;
            if (cfg.txnTimeoutIsSet)
                txn.SetTxnTimeout(cfg.TxnTimeout);

            return txn;
        }

        /// <summary>
        /// Flush the underlying memory pool, write a checkpoint record to the
        /// log, and then flush the log, even if there has been no activity
        /// since the last checkpoint.
        /// </summary>
        public void Checkpoint() {
            dbenv.txn_checkpoint(0, 0, DbConstants.DB_FORCE);
        }
        /// <summary>
        /// If there has been any logging activity in the database environment
        /// since the last checkpoint, flush the underlying memory pool, write a
        /// checkpoint record to the log, and then flush the log.
        /// </summary>
        /// <param name="kbytesWritten">
        /// A checkpoint will be done if more than kbytesWritten kilobytes of
        /// log data have been written since the last checkpoint.
        /// </param>
        /// <param name="minutesElapsed">
        /// A checkpoint will be done if more than minutesElapsed minutes have
        /// passed since the last checkpoint.
        /// </param>
        public void Checkpoint(uint kbytesWritten, uint minutesElapsed) {
            dbenv.txn_checkpoint(kbytesWritten, minutesElapsed, 0);
        }

        /// <summary>
        /// Close the Berkeley DB environment, freeing any allocated resources
        /// and closing any underlying subsystems. 
        /// </summary>
        /// <remarks>
        /// <para>
        /// The object should not be closed while any other handle that refers
        /// to it is not yet closed; for example, database environment handles
        /// must not be closed while database objects remain open, or
        /// transactions in the environment have not yet been committed or
        /// aborted.
        /// </para>
        /// <para>
        /// Where the environment was configured with
        /// <see cref="DatabaseEnvironmentConfig.UseTxns"/>, calling Close
        /// aborts any unresolved transactions. Applications should not depend
        /// on this behavior for transactions involving Berkeley DB databases;
        /// all such transactions should be explicitly resolved. The problem
        /// with depending on this semantic is that aborting an unresolved
        /// transaction involving database operations requires a database
        /// handle. Because the database handles should have been closed before
        /// calling Close, it will not be possible to abort the transaction, and
        /// recovery will have to be run on the Berkeley DB environment before
        /// further operations are done.
        /// </para>
        /// <para>
        /// In multithreaded applications, only a single thread may call Close.
        /// </para>
        /// </remarks>
        public void Close() {
            dbenv.close(0);
        }

        /// <summary>
        /// Run one iteration of the deadlock detector. The deadlock detector
        /// traverses the lock table and marks one of the participating lock
        /// requesters for rejection in each deadlock it finds.
        /// </summary>
        /// <param name="atype">Specify which lock request(s) to reject</param>
        /// <returns>The number of lock requests that were rejected.</returns>
        public uint DetectDeadlocks(DeadlockPolicy atype) {
            uint rejectCount = 0;
            if (atype == null)
                atype = DeadlockPolicy.DEFAULT;
            dbenv.lock_detect(0, atype.policy, ref rejectCount);
            return rejectCount;
        }

        /// <summary>
        /// Check for threads of control (either a true thread or a process)
        /// that have exited while manipulating Berkeley DB library data
        /// structures, while holding a logical database lock, or with an
        /// unresolved transaction (that is, a transaction that was never
        /// aborted or committed).
        /// </summary>
        /// <remarks>
        /// <para>
        /// For more information, see Architecting Data Store and Concurrent
        /// Data Store applications, and Architecting Transactional Data Store
        /// applications, both in the Berkeley DB Programmer's Reference Guide.
        /// </para>
        /// <para>
        /// FailCheck is based on the <see cref="SetThreadID"/> and
        /// <see cref="ThreadIsAlive"/> delegates. Applications calling
        /// FailCheck must have already set <see cref="ThreadIsAlive"/>, and
        /// must have configured <see cref="ThreadCount"/>.
        /// </para>
        /// <para>
        /// If FailCheck determines a thread of control exited while holding
        /// database read locks, it will release those locks. If FailCheck
        /// determines a thread of control exited with an unresolved
        /// transaction, the transaction will be aborted. In either of these
        /// cases, FailCheck will return successfully and the application may
        /// continue to use the database environment.
        /// </para>
        /// <para>
        /// In either of these cases, FailCheck will also report the process and
        /// thread IDs associated with any released locks or aborted
        /// transactions. The information is printed to a specified output
        /// channel (see <see cref="MessageFile"/> for more information), or
        /// passed to an application delegate (see <see cref="MessageCall"/> for
        /// more information).
        /// </para>
        /// <para>
        /// If FailCheck determines a thread of control has exited such that
        /// database environment recovery is required, it will throw
        /// <see cref="RunRecoveryException"/>. In this case, the application
        /// should not continue to use the database environment. For a further
        /// description as to the actions the application should take when this
        /// failure occurs, see Handling failure in Data Store and Concurrent
        /// Data Store applications, and Handling failure in Transactional Data
        /// Store applications, both in the Berkeley DB Programmer's Reference
        /// Guide.
        /// </para>
        /// </remarks>
        public void FailCheck() {
            dbenv.failchk(0);
        }

        /// <summary>
        /// Map an LSN object to a log filename
        /// </summary>
        /// <param name="logSeqNum">
        /// The DB_LSN structure for which a filename is wanted.
        /// </param>
        /// <returns>
        /// The name of the file containing the record named by
        /// <paramref name="logSeqNum"/>.
        /// </returns>
        public string LogFile(LSN logSeqNum) {
            return dbenv.log_file(LSN.getDB_LSN(logSeqNum));
        }

        /// <summary>
        /// Write all log records to disk.
        /// </summary>
        public void LogFlush() {
            LogFlush(null);
        }
        /// <summary>
        /// Write log records to disk.
        /// </summary>
        /// <param name="logSeqNum">
        /// All log records with LSN values less than or equal to
        /// <paramref name="logSeqNum"/> are written to disk. If null, all
        /// records in the log are flushed.
        /// </param>
        public void LogFlush(LSN logSeqNum) {
            dbenv.log_flush(LSN.getDB_LSN(logSeqNum));
        }

        /// <summary>
        /// Append a record to the log
        /// </summary>
        /// <param name="dbt">The record to write to the log.</param>
        /// <param name="flush">
        /// If true, the log is forced to disk after this record is written,
        /// guaranteeing that all records with LSN values less than or equal to
        /// the one being "put" are on disk before LogWrite returns.
        /// </param>
        /// <returns>The LSN of the written record</returns>
        public LSN LogWrite(DatabaseEntry dbt, bool flush) {
            DB_LSN lsn = new DB_LSN();

            dbenv.log_put(lsn,
                dbt, flush ? DbConstants.DB_FLUSH : 0);
            return new LSN(lsn.file, lsn.offset);
        }

        /// <summary>
        /// Set the panic state for the database environment. (Database
        /// environments in a panic state normally refuse all attempts to call
        /// Berkeley DB functions, throwing <see cref="RunRecoveryException"/>.)
        /// </summary>
        public void Panic() {
            dbenv.set_flags(DbConstants.DB_PANIC_ENVIRONMENT, 1);
        }

        /// <summary>
        /// Restore transactions that were prepared, but not yet resolved at the
        /// time of the system shut down or crash, to their state prior to the
        /// shut down or crash, including any locks previously held.
        /// </summary>
        /// <remarks>
        /// Calls to Recover from different threads of control should not be
        /// intermixed in the same environment.
        /// </remarks>
        /// <param name="count">
        /// The maximum number of <see cref="PreparedTransaction"/> objects
        /// to return.
        /// </param>
        /// <param name="resume">
        /// If true, continue returning a list of prepared, but not yet resolved
        /// transactions, starting where the last call to Recover left off.  If 
        /// false, begins a new pass over all prepared, but not yet completed
        /// transactions, regardless of whether they have already been returned
        /// in previous calls to Recover.
        /// </param>
        /// <returns>A list of the prepared transactions</returns>
        public PreparedTransaction[] Recover(uint count, bool resume) {
            uint flags = 0;
            flags |= resume ? DbConstants.DB_NEXT : DbConstants.DB_FIRST;
            
            return dbenv.txn_recover(count, flags);
        }

        /// <summary>
        /// Remove the underlying file represented by <paramref name="file"/>,
        /// incidentally removing all of the databases it contained.
        /// </summary>
        /// <param name="file">The physical file to be removed.</param>
        /// <param name="autoCommit">
        /// If true, enclose RemoveDB within a transaction. If the call
        /// succeeds, changes made by the operation will be recoverable. If the
        /// call fails, the operation will have made no changes.
        /// </param>
        public void RemoveDB(string file, bool autoCommit) {
            RemoveDB(file, null, autoCommit, null);
        }
        /// <summary>
        /// Remove the database specified by <paramref name="file"/> and
        /// <paramref name="database"/>.  If no database is specified, the
        /// underlying file represented by <paramref name="file"/> is removed,
        /// incidentally removing all of the databases it contained.
        /// </summary>
        /// <param name="file">
        /// The physical file which contains the database(s) to be removed.
        /// </param>
        /// <param name="database">The database to be removed.</param>
        /// <param name="autoCommit">
        /// If true, enclose RemoveDB within a transaction. If the call
        /// succeeds, changes made by the operation will be recoverable. If the
        /// call fails, the operation will have made no changes.
        /// </param>
        public void RemoveDB(string file, string database, bool autoCommit) {
            RemoveDB(file, database, autoCommit, null);
        }
        /// <summary>
        /// Remove the underlying file represented by <paramref name="file"/>,
        /// incidentally removing all of the databases it contained.
        /// </summary>
        /// <param name="file">The physical file to be removed.</param>
        /// <param name="autoCommit">
        /// If true, enclose RemoveDB within a transaction. If the call
        /// succeeds, changes made by the operation will be recoverable. If the
        /// call fails, the operation will have made no changes.
        /// </param>
        /// <param name="txn">
        /// If the operation is part of an application-specified transaction,
        /// <paramref name="txn"/> is a Transaction object returned from
        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
        /// the operation is part of a Berkeley DB Concurrent Data Store group,
        /// <paramref name="txn"/> is a handle returned from
        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.  If
        /// null, but <paramref name="autoCommit"/> or <see cref="AutoCommit"/>
        /// is true, the operation will be implicitly transaction protected. 
        /// </param>
        public void RemoveDB(string file, bool autoCommit, Transaction txn) {
            RemoveDB(file, null, autoCommit, txn);
        }
        /// <summary>
        /// Remove the database specified by <paramref name="file"/> and
        /// <paramref name="database"/>.  If no database is specified, the
        /// underlying file represented by <paramref name="file"/> is removed,
        /// incidentally removing all of the databases it contained.
        /// </summary>
        /// <param name="file">
        /// The physical file which contains the database(s) to be removed.
        /// </param>
        /// <param name="database">The database to be removed.</param>
        /// <param name="autoCommit">
        /// If true, enclose RemoveDB within a transaction. If the call
        /// succeeds, changes made by the operation will be recoverable. If the
        /// call fails, the operation will have made no changes.
        /// </param>
        /// <param name="txn">
        /// If the operation is part of an application-specified transaction,
        /// <paramref name="txn"/> is a Transaction object returned from
        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
        /// the operation is part of a Berkeley DB Concurrent Data Store group,
        /// <paramref name="txn"/> is a handle returned from
        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.  If
        /// null, but <paramref name="autoCommit"/> or <see cref="AutoCommit"/>
        /// is true, the operation will be implicitly transaction protected. 
        /// </param>
        public void RemoveDB(
            string file, string database, bool autoCommit, Transaction txn) {
            dbenv.dbremove(Transaction.getDB_TXN(txn),
                file, database, autoCommit ? DbConstants.DB_AUTO_COMMIT : 0);
        }

        /// <summary>
        /// Rename the underlying file represented by <paramref name="file"/>
        /// using the value supplied to <paramref name="newname"/>, incidentally
        /// renaming all of the databases it contained.
        /// </summary>
        /// <param name="file">The physical file to be renamed.</param>
        /// <param name="newname">The new name of the database or file.</param>
        /// <param name="autoCommit">
        /// If true, enclose RenameDB within a transaction. If the call
        /// succeeds, changes made by the operation will be recoverable. If the
        /// call fails, the operation will have made no changes.
        /// </param>
        public void RenameDB(string file, string newname, bool autoCommit) {
            RenameDB(file, null, newname, autoCommit, null);
        }
        /// <summary>
        /// Rename the database specified by <paramref name="file"/> and
        /// <paramref name="database"/> to <paramref name="newname"/>. If no
        /// database is specified, the underlying file represented by
        /// <paramref name="file"/> is renamed using the value supplied to
        /// <paramref name="newname"/>, incidentally renaming all of the
        /// databases it contained.
        /// </summary>
        /// <param name="file">
        /// The physical file which contains the database(s) to be renamed.
        /// </param>
        /// <param name="database">The database to be renamed.</param>
        /// <param name="newname">The new name of the database or file.</param>
        /// <param name="autoCommit">
        /// If true, enclose RenameDB within a transaction. If the call
        /// succeeds, changes made by the operation will be recoverable. If the
        /// call fails, the operation will have made no changes.
        /// </param>
        public void RenameDB(
            string file, string database, string newname, bool autoCommit) {
            RenameDB(file, database, newname, autoCommit, null);
        }
        /// <summary>
        /// Rename the underlying file represented by <paramref name="file"/>
        /// using the value supplied to <paramref name="newname"/>, incidentally
        /// renaming all of the databases it contained.
        /// </summary>
        /// <param name="file">The physical file to be renamed.</param>
        /// <param name="newname">The new name of the database or file.</param>
        /// <param name="autoCommit">
        /// If true, enclose RenameDB within a transaction. If the call
        /// succeeds, changes made by the operation will be recoverable. If the
        /// call fails, the operation will have made no changes.
        /// </param>
        /// <param name="txn">
        /// If the operation is part of an application-specified transaction,
        /// <paramref name="txn"/> is a Transaction object returned from
        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
        /// the operation is part of a Berkeley DB Concurrent Data Store group,
        /// <paramref name="txn"/> is a handle returned from
        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.  If
        /// null, but <paramref name="autoCommit"/> or <see cref="AutoCommit"/>
        /// is true, the operation will be implicitly transaction protected. 
        /// </param>
        public void RenameDB(string file,
            string newname, bool autoCommit, Transaction txn) {
            RenameDB(file, null, newname, autoCommit, txn);
        }
        /// <summary>
        /// Rename the database specified by <paramref name="file"/> and
        /// <paramref name="database"/> to <paramref name="newname"/>. If no
        /// database is specified, the underlying file represented by
        /// <paramref name="file"/> is renamed using the value supplied to
        /// <paramref name="newname"/>, incidentally renaming all of the
        /// databases it contained.
        /// </summary>
        /// <param name="file">
        /// The physical file which contains the database(s) to be renamed.
        /// </param>
        /// <param name="database">The database to be renamed.</param>
        /// <param name="newname">The new name of the database or file.</param>
        /// <param name="autoCommit">
        /// If true, enclose RenameDB within a transaction. If the call
        /// succeeds, changes made by the operation will be recoverable. If the
        /// call fails, the operation will have made no changes.
        /// </param>
        /// <param name="txn">
        /// If the operation is part of an application-specified transaction,
        /// <paramref name="txn"/> is a Transaction object returned from
        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
        /// the operation is part of a Berkeley DB Concurrent Data Store group,
        /// <paramref name="txn"/> is a handle returned from
        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.  If
        /// null, but <paramref name="autoCommit"/> or <see cref="AutoCommit"/>
        /// is true, the operation will be implicitly transaction protected. 
        /// </param>
        public void RenameDB(string file,
            string database, string newname, bool autoCommit, Transaction txn) {
            dbenv.dbrename(Transaction.getDB_TXN(txn), file,
                database, newname, autoCommit ? DbConstants.DB_AUTO_COMMIT : 0);
        }

        /// <summary>
        /// Allow database files to be copied, and then the copy used in the
        /// same database environment as the original.
        /// </summary>
        /// <remarks>
        /// <para>
        /// All databases contain an ID string used to identify the database in
        /// the database environment cache. If a physical database file is
        /// copied, and used in the same environment as another file with the
        /// same ID strings, corruption can occur. ResetFileID creates new ID
        /// strings for all of the databases in the physical file.
        /// </para>
        /// <para>
        /// ResetFileID modifies the physical file, in-place. Applications
        /// should not reset IDs in files that are currently in use.
        /// </para>
        /// </remarks>
        /// <param name="file">
        /// The name of the physical file in which new file IDs are to be created.
        /// </param>
        /// <param name="encrypted">
        /// If true, he file contains encrypted databases.
        /// </param>
        public void ResetFileID(string file, bool encrypted) {
            dbenv.fileid_reset(file, encrypted ? DbConstants.DB_ENCRYPT : 0);
        }

        /// <summary>
        /// Allow database files to be moved from one transactional database
        /// environment to another. 
        /// </summary>
        /// <remarks>
        /// <para>
        /// Database pages in transactional database environments contain
        /// references to the environment's log files (that is, log sequence
        /// numbers, or <see cref="LSN"/>s). Copying or moving a database file
        /// from one database environment to another, and then modifying it, can
        /// result in data corruption if the LSNs are not first cleared.
        /// </para>
        /// <para>
        /// Note that LSNs should be reset before moving or copying the database
        /// file into a new database environment, rather than moving or copying
        /// the database file and then resetting the LSNs. Berkeley DB has
        /// consistency checks that may be triggered if an application calls 
        /// ResetLSN on a database in a new environment when the database LSNs
        /// still reflect the old environment.
        /// </para>
        /// <para>
        /// The ResetLSN method modifies the physical file, in-place.
        /// Applications should not reset LSNs in files that are currently in
        /// use.
        /// </para>
        /// </remarks>
        /// <param name="file"></param>
        /// <param name="encrypted"></param>
        public void ResetLSN(string file, bool encrypted) {
            dbenv.lsn_reset(file, encrypted ? DbConstants.DB_ENCRYPT : 0);
        }

        /// <summary>
        /// Limit the number of sequential write operations scheduled by the
        /// library when flushing dirty pages from the cache.
        /// </summary>
        /// <param name="maxWrites">
        /// The maximum number of sequential write operations scheduled by the
        /// library when flushing dirty pages from the cache, or 0 if there is
        /// no limitation on the number of sequential write operations.
        /// </param>
        /// <param name="pause">
        /// The number of microseconds the thread of control should pause before
        /// scheduling further write operations. It must be specified as an
        /// unsigned 32-bit number of microseconds, limiting the maximum pause
        /// to roughly 71 minutes.
        /// </param>
        public void SetMaxSequentialWrites(int maxWrites, uint pause) {
            dbenv.set_mp_max_write(maxWrites, pause);
        }

        /// <summary>
        /// Flush all modified pages in the cache to their backing files. 
        /// </summary>
        /// <remarks>
        /// Pages in the cache that cannot be immediately written back to disk
        /// (for example, pages that are currently in use by another thread of
        /// control) are waited for and written to disk as soon as it is
        /// possible to do so.
        /// </remarks>
        public void SyncMemPool() {
            SyncMemPool(null);
        }
        /// <summary>
        /// Flush modified pages in the cache with log sequence numbers less 
        /// than <paramref name="minLSN"/> to their backing files. 
        /// </summary>
        /// <remarks>
        /// Pages in the cache that cannot be immediately written back to disk
        /// (for example, pages that are currently in use by another thread of
        /// control) are waited for and written to disk as soon as it is
        /// possible to do so.
        /// </remarks>
        /// <param name="minLSN">
        /// All modified pages with a log sequence number less than the minLSN
        /// parameter are written to disk. If null, all modified pages in the
        /// cache are written to disk.
        /// </param>
        public void SyncMemPool(LSN minLSN) {
            dbenv.memp_sync(LSN.getDB_LSN(minLSN));
        }

        /// <summary>
        /// Ensure that a specified percent of the pages in the cache are clean,
        /// by writing dirty pages to their backing files. 
        /// </summary>
        /// <param name="pctClean">
        /// The percent of the pages in the cache that should be clean.
        /// </param>
        /// <returns>
        /// The number of pages written to reach the specified percentage is
        /// copied.
        /// </returns>
        public int TrickleCleanMemPool(int pctClean) {
            int ret = 0;
            dbenv.memp_trickle(pctClean, ref ret);
            return ret;
        }

        /// <summary>
        /// Append an informational message to the Berkeley DB database
        /// environment log files. 
        /// </summary>
        /// <overloads>
        /// WriteToLog allows applications to include information in the
        /// database environment log files, for later review using the
        /// db_printlog  utility. This method is intended for debugging and
        /// performance tuning.
        /// </overloads>
        /// <param name="str">The message to append to the log files</param>
        public void WriteToLog(string str) {
            dbenv.log_printf(null, str);
        }
        /// <summary>
        /// Append an informational message to the Berkeley DB database
        /// environment log files. 
        /// </summary>
        /// <overloads>
        /// WriteToLog allows applications to include information in the
        /// database environment log files, for later review using the
        /// db_printlog  utility. This method is intended for debugging and
        /// performance tuning.
        /// </overloads>
        /// <param name="str">The message to append to the log files</param>
        /// <param name="txn">
        /// If the operation is part of an application-specified transaction,
        /// <paramref name="txn"/> is a Transaction object returned from
        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; 
        /// otherwise null.
        /// </param>
        public void WriteToLog(string str, Transaction txn) {
            dbenv.log_printf(Transaction.getDB_TXN(txn), str);
        }

        #region Stats
        /// <summary>
        /// The locking subsystem statistics
        /// </summary>
        /// <returns>The locking subsystem statistics</returns>
        public LockStats LockingSystemStats() {
            return LockingSystemStats(false);
        }
        /// <summary>
        /// The locking subsystem statistics
        /// </summary>
        /// <param name="clearStats">
        /// If true, reset statistics after returning their values.
        /// </param>
        /// <returns>The locking subsystem statistics</returns>
        public LockStats LockingSystemStats(bool clearStats) {
            uint flags = 0;
            flags |= clearStats ? DbConstants.DB_STAT_CLEAR : 0;
            LockStatStruct st = dbenv.lock_stat(flags);
            return new LockStats(st);
        }
        /// <summary>
        /// The logging subsystem statistics
        /// </summary>
        /// <returns>The logging subsystem statistics</returns>
        public LogStats LoggingSystemStats() {
            return LoggingSystemStats(false);
        }
        /// <summary>
        /// The logging subsystem statistics
        /// </summary>
        /// <param name="clearStats">
        /// If true, reset statistics after returning their values.
        /// </param>
        /// <returns>The logging subsystem statistics</returns>
        public LogStats LoggingSystemStats(bool clearStats) {
            uint flags = 0;
            flags |= clearStats ? DbConstants.DB_STAT_CLEAR : 0;
            LogStatStruct st = dbenv.log_stat(flags);
            return new LogStats(st);
        }
        /// <summary>
        /// The memory pool (that is, the buffer cache) subsystem statistics
        /// </summary>
        /// <returns>The memory pool subsystem statistics</returns>
        public MPoolStats MPoolSystemStats() {
            return MPoolSystemStats(false);
        }
        /// <summary>
        /// The memory pool (that is, the buffer cache) subsystem statistics
        /// </summary>
        /// <param name="clearStats">
        /// If true, reset statistics after returning their values.
        /// </param>
        /// <returns>The memory pool subsystem statistics</returns>
        public MPoolStats MPoolSystemStats(bool clearStats) {
            uint flags = 0;
            flags |= clearStats ? DbConstants.DB_STAT_CLEAR : 0;
            MempStatStruct st = dbenv.memp_stat(flags);
            return new MPoolStats(st);
        }
        /// <summary>
        /// The mutex subsystem statistics
        /// </summary>
        /// <returns>The mutex subsystem statistics</returns>
        public MutexStats MutexSystemStats() {
            return MutexSystemStats(false);
        }
        /// <summary>
        /// The mutex subsystem statistics
        /// </summary>
        /// <param name="clearStats">
        /// If true, reset statistics after returning their values.
        /// </param>
        /// <returns>The mutex subsystem statistics</returns>
        public MutexStats MutexSystemStats(bool clearStats) {
            uint flags = 0;
            flags |= clearStats ? DbConstants.DB_STAT_CLEAR : 0;
            MutexStatStruct st = dbenv.mutex_stat(flags);
            return new MutexStats(st);
        }
        /// <summary>
        /// The replication manager statistics
        /// </summary>
        /// <returns>The replication manager statistics</returns>
        public RepMgrStats RepMgrSystemStats() {
            return RepMgrSystemStats(false);
        }
        /// <summary>
        /// The replication manager statistics
        /// </summary>
        /// <param name="clearStats">
        /// If true, reset statistics after returning their values.
        /// </param>
        /// <returns>The replication manager statistics</returns>
        public RepMgrStats RepMgrSystemStats(bool clearStats) {
            uint flags = 0;
            flags |= clearStats ? DbConstants.DB_STAT_CLEAR : 0;
            RepMgrStatStruct st = dbenv.repmgr_stat(flags);
            return new RepMgrStats(st);
        }
        /// <summary>
        /// The replication subsystem statistics
        /// </summary>
        /// <returns>The replication subsystem statistics</returns>
        public ReplicationStats ReplicationSystemStats() {
            return ReplicationSystemStats(false);
        }
        /// <summary>
        /// The replication subsystem statistics
        /// </summary>
        /// <param name="clearStats">
        /// If true, reset statistics after returning their values.
        /// </param>
        /// <returns>The replication subsystem statistics</returns>
        public ReplicationStats ReplicationSystemStats(bool clearStats) {
            uint flags = 0;
            flags |= clearStats ? DbConstants.DB_STAT_CLEAR : 0;
            ReplicationStatStruct st = dbenv.rep_stat(flags);
            return new ReplicationStats(st);
        }
        /// <summary>
        /// The transaction subsystem statistics
        /// </summary>
        /// <returns>The transaction subsystem statistics</returns>
        public TransactionStats TransactionSystemStats() {
            return TransactionSystemStats(false);
        }
        /// <summary>
        /// The transaction subsystem statistics
        /// </summary>
        /// <param name="clearStats">
        /// If true, reset statistics after returning their values.
        /// </param>
        /// <returns>The transaction subsystem statistics</returns>
        public TransactionStats TransactionSystemStats(bool clearStats) {
            uint flags = 0;
            flags |= clearStats ? DbConstants.DB_STAT_CLEAR : 0;
            TxnStatStruct st = dbenv.txn_stat(flags);
            return new TransactionStats(st);
        }
        #endregion Stats

        #region Print Stats
        /// <summary>
        /// Display the locking subsystem statistical information, as described
        /// by <see cref="LockStats"/>.
        /// </summary>
        public void PrintLockingSystemStats() {
            PrintLockingSystemStats(false, false, false, false, false, false);
        }
        /// <summary>
        /// Display the locking subsystem statistical information, as described
        /// by <see cref="LockStats"/>.
        /// </summary>
        /// <param name="PrintAll">
		/// If true, display all available information.
		/// </param>
        /// <param name="ClearStats">
        /// If true, reset statistics after displaying their values.
        /// </param>
        public void PrintLockingSystemStats(bool PrintAll, bool ClearStats) {
            PrintLockingSystemStats(
                PrintAll, ClearStats, false, false, false, false);
        }
        /// <summary>
        /// Display the locking subsystem statistical information, as described
        /// by <see cref="LockStats"/>.
        /// </summary>
        /// <param name="PrintAll">
		/// If true, display all available information.
		/// </param>
        /// <param name="ClearStats">
        /// If true, reset statistics after displaying their values.
        /// </param>
        /// <param name="ConflictMatrix">
        /// If true, display the lock conflict matrix.
        /// </param>
        /// <param name="Lockers">
        /// If true, Display the lockers within hash chains.
        /// </param>
        /// <param name="Objects">
        /// If true, display the lock objects within hash chains.
        /// </param>
        /// <param name="Parameters">
        /// If true, display the locking subsystem parameters.
        /// </param>
        public void PrintLockingSystemStats(bool PrintAll, bool ClearStats,
            bool ConflictMatrix, bool Lockers, bool Objects, bool Parameters) {
            uint flags = 0;
            flags |= PrintAll ? DbConstants.DB_STAT_ALL : 0;
            flags |= ClearStats ? DbConstants.DB_STAT_CLEAR : 0;
            flags |= ConflictMatrix ? DbConstants.DB_STAT_LOCK_CONF : 0;
            flags |= Lockers ? DbConstants.DB_STAT_LOCK_LOCKERS : 0;
            flags |= Objects ? DbConstants.DB_STAT_LOCK_OBJECTS : 0;
            flags |= Parameters ? DbConstants.DB_STAT_LOCK_PARAMS : 0;

            dbenv.lock_stat_print(flags);
        }

        /// <summary>
        /// Display the logging subsystem statistical information, as described
        /// by <see cref="LogStats"/>.
        /// </summary>
        public void PrintLoggingSystemStats() {
            PrintLoggingSystemStats(false, false);
        }
        /// <summary>
        /// Display the logging subsystem statistical information, as described
        /// by <see cref="LogStats"/>.
        /// </summary>
        /// <param name="PrintAll">
		/// If true, display all available information.
		/// </param>
        /// <param name="ClearStats">
        /// If true, reset statistics after displaying their values.
        /// </param>
        public void PrintLoggingSystemStats(bool PrintAll, bool ClearStats) {
            uint flags = 0;
            flags |= PrintAll ? DbConstants.DB_STAT_ALL : 0;
            flags |= ClearStats ? DbConstants.DB_STAT_CLEAR : 0;

            dbenv.log_stat_print(flags);
        }

        /// <summary>
        /// Display the memory pool (that is, buffer cache) subsystem
        /// statistical information, as described by <see cref="MPoolStats"/>.
        /// </summary>
        public void PrintMPoolSystemStats() {
            PrintMPoolSystemStats(false, false, false);
        }
        /// <summary>
        /// Display the memory pool (that is, buffer cache) subsystem
        /// statistical information, as described by <see cref="MPoolStats"/>.
        /// </summary>
        /// <param name="PrintAll">
		/// If true, display all available information.
		/// </param>
        /// <param name="ClearStats">
        /// If true, reset statistics after displaying their values.
        /// </param>
        public void PrintMPoolSystemStats(bool PrintAll, bool ClearStats) {
            PrintMPoolSystemStats(PrintAll, ClearStats, false);
        }
        /// <summary>
        /// Display the memory pool (that is, buffer cache) subsystem
        /// statistical information, as described by <see cref="MPoolStats"/>.
        /// </summary>
        /// <param name="PrintAll">
        /// If true, display all available information.
        /// </param>
        /// <param name="ClearStats">
        /// If true, reset statistics after displaying their values.
        /// </param>
        /// <param name="HashChains">
        /// If true, display the buffers with hash chains.
        /// </param>
        public void PrintMPoolSystemStats(
            bool PrintAll, bool ClearStats, bool HashChains) {
            uint flags = 0;
            flags |= PrintAll ? DbConstants.DB_STAT_ALL : 0;
            flags |= ClearStats ? DbConstants.DB_STAT_CLEAR : 0;
            flags |= HashChains ? DbConstants.DB_STAT_MEMP_HASH : 0;

            dbenv.memp_stat_print(flags);
        }

        /// <summary>
        /// Display the mutex subsystem statistical information, as described
        /// by <see cref="MutexStats"/>.
        /// </summary>
        public void PrintMutexSystemStats() {
            PrintMutexSystemStats(false, false);
        }
        /// <summary>
        /// Display the mutex subsystem statistical information, as described
        /// by <see cref="MutexStats"/>.
        /// </summary>
        /// <param name="PrintAll">
		/// If true, display all available information.
		/// </param>
        /// <param name="ClearStats">
        /// If true, reset statistics after displaying their values.
        /// </param>
        public void PrintMutexSystemStats(bool PrintAll, bool ClearStats) {
            uint flags = 0;
            flags |= PrintAll ? DbConstants.DB_STAT_ALL : 0;
            flags |= ClearStats ? DbConstants.DB_STAT_CLEAR : 0;

            dbenv.mutex_stat_print(flags);
        }

        /// <summary>
        /// Display the replication manager statistical information, as
        /// described by <see cref="RepMgrStats"/>.
        /// </summary>
        public void PrintRepMgrSystemStats() {
            PrintRepMgrSystemStats(false, false);
        }
        /// <summary>
        /// Display the replication manager statistical information, as
        /// described by <see cref="RepMgrStats"/>.
        /// </summary>
        /// <param name="PrintAll">
		/// If true, display all available information.
		/// </param>
        /// <param name="ClearStats">
        /// If true, reset statistics after displaying their values.
        /// </param>
        public void PrintRepMgrSystemStats(bool PrintAll, bool ClearStats) {
            uint flags = 0;
            flags |= PrintAll ? DbConstants.DB_STAT_ALL : 0;
            flags |= ClearStats ? DbConstants.DB_STAT_CLEAR : 0;

            dbenv.repmgr_stat_print(flags);
        }

        /// <summary>
        /// Display the replication subsystem statistical information, as
        /// described by <see cref="ReplicationStats"/>.
        /// </summary>
        public void PrintReplicationSystemStats() {
            PrintReplicationSystemStats(false, false);
        }
        /// <summary>
        /// Display the replication subsystem statistical information, as
        /// described by <see cref="ReplicationStats"/>.
        /// </summary>
        /// <param name="PrintAll">
		/// If true, display all available information.
		/// </param>
        /// <param name="ClearStats">
        /// If true, reset statistics after displaying their values.
        /// </param>
        public void 
            PrintReplicationSystemStats(bool PrintAll, bool ClearStats) {
            uint flags = 0;
            flags |= PrintAll ? DbConstants.DB_STAT_ALL : 0;
            flags |= ClearStats ? DbConstants.DB_STAT_CLEAR : 0;

            dbenv.rep_stat_print(flags);
        }

        /// <summary>
        /// Display the locking subsystem statistical information, as described
        /// by <see cref="LockStats"/>.
        /// </summary>
        public void PrintStats() {
            PrintStats(false, false, false);
        }
        /// <summary>
        /// Display the locking subsystem statistical information, as described
        /// by <see cref="LockStats"/>.
        /// </summary>
        /// <param name="PrintAll">
		/// If true, display all available information.
		/// </param>
        /// <param name="ClearStats">
        /// If true, reset statistics after displaying their values.
        /// </param>
        public void PrintStats(bool PrintAll, bool ClearStats) {
            PrintStats(PrintAll, ClearStats, false);
        }
        /// <summary>
        /// Display the locking subsystem statistical information, as described
        /// by <see cref="LockStats"/>.
        /// </summary>
        public void PrintSubsystemStats() {
            PrintStats(false, false, true);
        }
        /// <summary>
        /// Display the locking subsystem statistical information, as described
        /// by <see cref="LockStats"/>.
        /// </summary>
        /// <param name="PrintAll">
		/// If true, display all available information.
		/// </param>
        /// <param name="ClearStats">
        /// If true, reset statistics after displaying their values.
        /// </param>
        public void PrintSubsystemStats(bool PrintAll, bool ClearStats) {
            PrintStats(PrintAll, ClearStats, true);
        }
        /// <summary>
        /// Display the locking subsystem statistical information, as described
        /// by <see cref="LockStats"/>.
        /// </summary>
        private void PrintStats(bool all, bool clear, bool subs) {
            uint flags = 0;
            flags |= all ? DbConstants.DB_STAT_ALL : 0;
            flags |= clear ? DbConstants.DB_STAT_CLEAR : 0;
            flags |= subs ? DbConstants.DB_STAT_SUBSYSTEM : 0;
            dbenv.stat_print(flags);
        }

        /// <summary>
        /// Display the transaction subsystem statistical information, as
        /// described by <see cref="TransactionStats"/>.
        /// </summary>
        public void PrintTransactionSystemStats() {
            PrintTransactionSystemStats(false, false);
        }
        /// <summary>
        /// Display the transaction subsystem statistical information, as
        /// described by <see cref="TransactionStats"/>.
        /// </summary>
        /// <param name="PrintAll">
		/// If true, display all available information.
		/// </param>
        /// <param name="ClearStats">
        /// If true, reset statistics after displaying their values.
        /// </param>
        public void
            PrintTransactionSystemStats(bool PrintAll, bool ClearStats) {
            uint flags = 0;
            flags |= PrintAll ? DbConstants.DB_STAT_ALL : 0;
            flags |= ClearStats ? DbConstants.DB_STAT_CLEAR : 0;

            dbenv.txn_stat_print(flags);
        }
        #endregion Print Stats

        private uint getRepTimeout(int which) {
            uint ret = 0;
            dbenv.rep_get_timeout(which, ref ret);
            return ret;
        }
        private bool getRepConfig(uint which) {
            int onoff = 0;
            dbenv.rep_get_config(which, ref onoff);
            return (onoff != 0);
        }

        #region Unsupported Subsystem Methods
        /// <summary>
        /// The Berkeley DB process' environment may be permitted to specify
        /// information to be used when naming files; see Berkeley DB File
        /// Naming in the Programmer's Reference Guide for more information.
        /// </summary>
        public bool UseEnvironmentVars {
            get {
                uint flags = 0;
                dbenv.get_open_flags(ref flags);
                return (flags & DbConstants.DB_USE_ENVIRON) != 0;
            }
        }
        private bool USE_ENVIRON_ROOT {
            get {
                uint flags = 0;
                dbenv.get_open_flags(ref flags);
                return (flags & DbConstants.DB_USE_ENVIRON_ROOT) != 0;
            }
        }
        private uint CreateLockerID() {
            uint ret = 0;
            dbenv.lock_id(ref ret);
            return ret;
        }
        private void FreeLockerID(uint locker) {
            dbenv.lock_id_free(locker);
        }

        private Lock GetLock(
            uint locker, bool wait, DatabaseEntry obj, LockMode mode) {
            return new Lock(dbenv.lock_get(locker,
                wait ? 0 : DbConstants.DB_LOCK_NOWAIT,
                DatabaseEntry.getDBT(obj), LockMode.GetMode(mode)));
        }

        private Mutex GetMutex(bool SelfBlock, bool SingleProcess) {
            uint m = 0;
            uint flags = 0;
            flags |= SelfBlock ? DbConstants.DB_MUTEX_SELF_BLOCK : 0;
            flags |= SingleProcess ? DbConstants.DB_MUTEX_PROCESS_ONLY : 0;
            dbenv.mutex_alloc(flags, ref m);
            return new Mutex(this, m);
        }

        private void LockMany(uint locker, bool wait, LockRequest[] vec) {
            LockMany(locker, wait, vec, null);
        }
        private void LockMany(
            uint locker, bool wait, LockRequest[] vec, LockRequest failedReq) {
            IntPtr[] reqList = new IntPtr[vec.Length];
            DB_LOCKREQ[] lst = new DB_LOCKREQ[vec.Length];

            for (int i = 0; i < vec.Length; i++) {
                reqList[i] = DB_LOCKREQ.getCPtr(
                    LockRequest.get_DB_LOCKREQ(vec[i])).Handle;
                lst[i] = LockRequest.get_DB_LOCKREQ(vec[i]);
            }

            dbenv.lock_vec(locker, wait ? 0 : DbConstants.DB_TXN_NOWAIT,
                reqList, vec.Length, LockRequest.get_DB_LOCKREQ(failedReq));

        }
        private void PutLock(Lock lck) {
            dbenv.lock_put(Lock.GetDB_LOCK(lck));
        }
        #endregion
    }
}