/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2009 Oracle. All rights reserved. * */ using System; using System.Collections.Generic; using System.Text; using BerkeleyDB.Internal; namespace BerkeleyDB { /// <summary> /// A class that provides an arbitrary number of persistent objects that /// return an increasing or decreasing sequence of integers. /// </summary> public class Sequence : IDisposable { private DB_SEQUENCE seq; private bool isOpen; /// <summary> /// Instantiate a new Sequence object. /// </summary> /// <remarks> /// If <paramref name="txn"/> is null and the operation occurs in a /// transactional database, the operation will be implicitly transaction /// protected. /// </remarks> /// <param name="cfg">Configuration parameters for the Sequence</param> public Sequence(SequenceConfig cfg) : this(cfg, null) { } /// <summary> /// Instantiate a new Sequence object. /// </summary> /// <remarks> /// If <paramref name="txn"/> is null and the operation occurs in a /// transactional database, the operation will be implicitly transaction /// protected. /// </remarks> /// <param name="cfg">Configuration parameters for the Sequence</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. /// </param> public Sequence(SequenceConfig cfg, Transaction txn) { seq = new DB_SEQUENCE(cfg.BackingDatabase.db, 0); if (cfg.initialValIsSet) seq.initial_value(cfg.InitialValue); seq.set_flags(cfg.flags); if (cfg.rangeIsSet) seq.set_range(cfg.Min, cfg.Max); if (cfg.cacheSzIsSet) seq.set_cachesize(cfg.CacheSize); seq.open(Transaction.getDB_TXN(txn), cfg.key, cfg.openFlags); isOpen = true; } /// <summary> /// Close the sequence handle. Any unused cached values are lost. /// </summary> public void Close() { isOpen = false; seq.close(0); } /// <summary> /// Return the next available element in the sequence and change the /// sequence value by <paramref name="Delta"/>. /// </summary> /// <overloads> /// <para> /// If there are enough cached values in the sequence handle then they /// will be returned. Otherwise the next value will be fetched from the /// database and incremented (decremented) by enough to cover the delta /// and the next batch of cached values. /// </para> /// <para> /// For maximum concurrency a non-zero cache size should be specified /// prior to opening the sequence handle and <paramref name="NoSync"/> /// should be specified for each Get method call. /// </para> /// <para> /// By default, sequence ranges do not wrap; to cause the sequence to /// wrap around the beginning or end of its range, set /// <paramref name="SequenceConfig.Wrap"/> to true. /// </para> /// <para> /// If <paramref name="P:BackingDatabase"/> was opened in a transaction, /// calling Get may result in changes to the sequence object; these /// changes will be automatically committed in a transaction internal to /// the Berkeley DB library. If the thread of control calling Get has an /// active transaction, which holds locks on the same database as the /// one in which the sequence object is stored, it is possible for a /// thread of control calling Get to self-deadlock because the active /// transaction's locks conflict with the internal transaction's locks. /// For this reason, it is often preferable for sequence objects to be /// stored in their own database. /// </para> /// </overloads> /// <param name="Delta"> /// The amount by which to increment the sequence value. Must be /// greater than 0. /// </param> /// <returns>The next available element in the sequence.</returns> public Int64 Get(int Delta) { return Get(Delta, false, null); } /// <summary> /// Return the next available element in the sequence and change the /// sequence value by <paramref name="Delta"/>. /// </summary> /// <param name="Delta"> /// The amount by which to increment the sequence value. Must be /// greater than 0. /// </param> /// <param name="NoSync"> /// If true, and if the operation is implicitly transaction protected, /// do not synchronously flush the log when the transaction commits. /// </param> /// <returns>The next available element in the sequence.</returns> public Int64 Get(int Delta, bool NoSync) { return Get(Delta, NoSync, null); } /// <summary> /// Return the next available element in the sequence and change the /// sequence value by <paramref name="Delta"/>. /// </summary> /// <param name="Delta"> /// The amount by which to increment the sequence value. Must be /// greater than 0. /// </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. /// Must be null if the sequence was opened with a non-zero cache size. /// </param> /// <returns>The next available element in the sequence.</returns> public Int64 Get(int Delta, Transaction txn) { return Get(Delta, false, txn); } private Int64 Get(int Delta, bool NoSync, Transaction txn) { Int64 ret = DbConstants.DB_AUTO_COMMIT; uint flags = NoSync ? DbConstants.DB_TXN_NOSYNC : 0; seq.get(Transaction.getDB_TXN(txn), Delta, ref ret, flags); return ret; } /// <summary> /// The database used by the sequence. /// </summary> public Database BackingDatabase { get { return Database.fromDB(seq.get_db()); } } /// <summary> /// The key for the sequence. /// </summary> public DatabaseEntry Key { get { DatabaseEntry ret = new DatabaseEntry(); seq.get_key(ret); return ret; } } /// <summary> /// Print diagnostic information. /// </summary> public void PrintStats() { PrintStats(false); } /// <summary> /// Print diagnostic information. /// </summary> /// <overloads> /// The diagnostic information is described by /// <see cref="SequenceStats"/>. /// </overloads> /// <param name="ClearStats"> /// If true, reset statistics after printing. /// </param> public void PrintStats(bool ClearStats) { uint flags = 0; flags |= ClearStats ? DbConstants.DB_STAT_CLEAR : 0; seq.stat_print(flags); } /// <summary> /// Remove the sequence from the database. /// </summary> public void Remove() { Remove(false, null); } /// <summary> /// Remove the sequence from the database. /// </summary> /// <param name="NoSync"> /// If true, and if the operation is implicitly transaction protected, /// do not synchronously flush the log when the transaction commits. /// </param> public void Remove(bool NoSync) { Remove(NoSync, null); } /// <summary> /// Remove the sequence from the database. /// </summary> /// <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. /// </param> public void Remove(Transaction txn) { Remove(false, txn); } private void Remove(bool NoSync, Transaction txn) { uint flags = NoSync ? DbConstants.DB_TXN_NOSYNC : 0; isOpen = false; seq.remove(Transaction.getDB_TXN(txn), flags); } /// <summary> /// Return statistical information for this sequence. /// </summary> /// <returns>Statistical information for this sequence.</returns> public SequenceStats Stats() { return Stats(false); } /// <summary> /// Return statistical information for this sequence. /// </summary> /// <overloads> /// <para> /// In the presence of multiple threads or processes accessing an active /// sequence, the information returned by DB_SEQUENCE->stat() may be /// out-of-date. /// </para> /// <para> /// The DB_SEQUENCE->stat() method cannot be transaction-protected. For /// this reason, it should be called in a thread of control that has no /// open cursors or active transactions. /// </para> /// </overloads> /// <param name="clear">If true, reset statistics.</param> /// <returns>Statistical information for this sequence.</returns> public SequenceStats Stats(bool clear) { uint flags = 0; flags |= clear ? DbConstants.DB_STAT_CLEAR : 0; SequenceStatStruct st = seq.stat(flags); return new SequenceStats(st); } /// <summary> /// Release the resources held by this object, and close the sequence if /// it's still open. /// </summary> public void Dispose() { if (isOpen) seq.close(0); seq.Dispose(); GC.SuppressFinalize(this); } /// <summary> /// The current cache size. /// </summary> public int Cachesize { get { int ret = 0; seq.get_cachesize(ref ret); return ret; } } /// <summary> /// The minimum value in the sequence. /// </summary> public Int64 Min { get { Int64 ret = 0; Int64 tmp = 0; seq.get_range(ref ret, ref tmp); return ret; } } /// <summary> /// The maximum value in the sequence. /// </summary> public Int64 Max { get { Int64 ret = 0; Int64 tmp = 0; seq.get_range(ref tmp, ref ret); return ret; } } /// <summary> /// If true, the sequence should wrap around when it is incremented /// (decremented) past the specified maximum (minimum) value. /// </summary> public bool Wrap { get { uint flags = 0; seq.get_flags(ref flags); return (flags & DbConstants.DB_SEQ_WRAP) != 0; } } /// <summary> /// If true, the sequence will be incremented. This is the default. /// </summary> public bool Increment { get { uint flags = 0; seq.get_flags(ref flags); return (flags & DbConstants.DB_SEQ_INC) != 0; } } /// <summary> /// If true, the sequence will be decremented. /// </summary> public bool Decrement { get { uint flags = 0; seq.get_flags(ref flags); return (flags & DbConstants.DB_SEQ_DEC) != 0; } } } }