1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2009 Oracle.  All rights reserved.
5 *
6 */
7using System;
8using System.Collections.Generic;
9using System.Text;
10using BerkeleyDB.Internal;
11
12namespace BerkeleyDB {
13    /// <summary>
14    /// A class that provides an arbitrary number of persistent objects that
15    /// return an increasing or decreasing sequence of integers.
16    /// </summary>
17    public class Sequence : IDisposable {
18        private DB_SEQUENCE seq;
19        private bool isOpen;
20
21        /// <summary>
22        /// Instantiate a new Sequence object.
23        /// </summary>
24        /// <remarks>
25        /// If <paramref name="txn"/> is null and the operation occurs in a
26        /// transactional database, the operation will be implicitly transaction
27        /// protected.
28        /// </remarks>
29        /// <param name="cfg">Configuration parameters for the Sequence</param>
30        public Sequence(SequenceConfig cfg) : this(cfg, null) { }
31        /// <summary>
32        /// Instantiate a new Sequence object.
33        /// </summary>
34        /// <remarks>
35        /// If <paramref name="txn"/> is null and the operation occurs in a
36        /// transactional database, the operation will be implicitly transaction
37        /// protected.
38        /// </remarks>
39        /// <param name="cfg">Configuration parameters for the Sequence</param>
40        /// <param name="txn">
41        /// If the operation is part of an application-specified transaction,
42        /// <paramref name="txn"/> is a Transaction object returned from
43        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
44        /// the operation is part of a Berkeley DB Concurrent Data Store group,
45        /// <paramref name="txn"/> is a handle returned from
46        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
47        /// </param>
48        public Sequence(SequenceConfig cfg, Transaction txn) {
49            seq = new DB_SEQUENCE(cfg.BackingDatabase.db, 0);
50            if (cfg.initialValIsSet)
51                seq.initial_value(cfg.InitialValue);
52            seq.set_flags(cfg.flags);
53            if (cfg.rangeIsSet)
54                seq.set_range(cfg.Min, cfg.Max);
55            if (cfg.cacheSzIsSet)
56                seq.set_cachesize(cfg.CacheSize);
57            seq.open(Transaction.getDB_TXN(txn),
58                cfg.key, cfg.openFlags);
59            isOpen = true;
60        }
61
62        /// <summary>
63        /// Close the sequence handle. Any unused cached values are lost.
64        /// </summary>
65        public void Close() {
66            isOpen = false;
67            seq.close(0);
68        }
69
70        /// <summary>
71        /// Return the next available element in the sequence and change the
72        /// sequence value by <paramref name="Delta"/>.
73        /// </summary>
74        /// <overloads>
75        /// <para>
76        /// If there are enough cached values in the sequence handle then they
77        /// will be returned. Otherwise the next value will be fetched from the
78        /// database and incremented (decremented) by enough to cover the delta
79        /// and the next batch of cached values.
80        /// </para>
81        /// <para>
82        /// For maximum concurrency a non-zero cache size should be specified
83        /// prior to opening the sequence handle and <paramref name="NoSync"/>
84        /// should be specified for each Get method call.
85        /// </para>
86		/// <para>
87        /// By default, sequence ranges do not wrap; to cause the sequence to
88        /// wrap around the beginning or end of its range, set
89        /// <paramref name="SequenceConfig.Wrap"/> to true.
90        /// </para>
91        /// <para>
92        /// If <paramref name="P:BackingDatabase"/> was opened in a transaction,
93        /// calling Get may result in changes to the sequence object; these
94        /// changes will be automatically committed in a transaction internal to
95        /// the Berkeley DB library. If the thread of control calling Get has an
96        /// active transaction, which holds locks on the same database as the
97        /// one in which the sequence object is stored, it is possible for a
98        /// thread of control calling Get to self-deadlock because the active
99        /// transaction's locks conflict with the internal transaction's locks.
100        /// For this reason, it is often preferable for sequence objects to be
101        /// stored in their own database.
102        /// </para>
103        /// </overloads>
104        /// <param name="Delta">
105        /// The amount by which to increment the sequence value.  Must be
106        /// greater than 0.
107        /// </param>
108        /// <returns>The next available element in the sequence.</returns>
109        public Int64 Get(int Delta) {
110            return Get(Delta, false, null);
111        }
112        /// <summary>
113        /// Return the next available element in the sequence and change the
114        /// sequence value by <paramref name="Delta"/>.
115        /// </summary>
116        /// <param name="Delta">
117        /// The amount by which to increment the sequence value.  Must be
118        /// greater than 0.
119        /// </param>
120        /// <param name="NoSync">
121        /// If true, and if the operation is implicitly transaction protected,
122        /// do not synchronously flush the log when the transaction commits.
123        /// </param>
124        /// <returns>The next available element in the sequence.</returns>
125        public Int64 Get(int Delta, bool NoSync) {
126            return Get(Delta, NoSync, null);
127        }
128        /// <summary>
129        /// Return the next available element in the sequence and change the
130        /// sequence value by <paramref name="Delta"/>.
131        /// </summary>
132        /// <param name="Delta">
133        /// The amount by which to increment the sequence value.  Must be
134        /// greater than 0.
135        /// </param>
136        /// <param name="txn">
137        /// If the operation is part of an application-specified transaction,
138        /// <paramref name="txn"/> is a Transaction object returned from
139        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
140        /// the operation is part of a Berkeley DB Concurrent Data Store group,
141        /// <paramref name="txn"/> is a handle returned from
142        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
143        /// Must be null if the sequence was opened with a non-zero cache size.
144        /// </param>
145        /// <returns>The next available element in the sequence.</returns>
146        public Int64 Get(int Delta, Transaction txn) {
147            return Get(Delta, false, txn);
148        }
149        private Int64 Get(int Delta, bool NoSync, Transaction txn) {
150            Int64 ret = DbConstants.DB_AUTO_COMMIT;
151            uint flags = NoSync ? DbConstants.DB_TXN_NOSYNC : 0;
152            seq.get(Transaction.getDB_TXN(txn), Delta, ref ret, flags);
153            return ret;
154        }
155
156        /// <summary>
157        /// The database used by the sequence.
158        /// </summary>
159        public Database BackingDatabase {
160            get { return Database.fromDB(seq.get_db()); }
161        }
162
163        /// <summary>
164        /// The key for the sequence.
165        /// </summary>
166        public DatabaseEntry Key {
167            get {
168                DatabaseEntry ret = new DatabaseEntry();
169                seq.get_key(ret);
170                return ret;
171            }
172        }
173
174        /// <summary>
175        /// Print diagnostic information.
176        /// </summary>
177        public void PrintStats() {
178            PrintStats(false);
179        }
180        /// <summary>
181        /// Print diagnostic information.
182        /// </summary>
183        /// <overloads>
184        /// The diagnostic information is described by
185        /// <see cref="SequenceStats"/>.
186        /// </overloads>
187        /// <param name="ClearStats">
188        /// If true, reset statistics after printing.
189        /// </param>
190        public void PrintStats(bool ClearStats) {
191            uint flags = 0;
192            flags |= ClearStats ? DbConstants.DB_STAT_CLEAR : 0;
193            seq.stat_print(flags);
194        }
195
196        /// <summary>
197        /// Remove the sequence from the database.
198        /// </summary>
199        public void Remove() {
200            Remove(false, null);
201        }
202        /// <summary>
203        /// Remove the sequence from the database.
204        /// </summary>
205        /// <param name="NoSync">
206        /// If true, and if the operation is implicitly transaction protected,
207        /// do not synchronously flush the log when the transaction commits.
208        /// </param>
209        public void Remove(bool NoSync) {
210            Remove(NoSync, null);
211        }
212        /// <summary>
213        /// Remove the sequence from the database.
214        /// </summary>
215        /// <param name="txn">
216        /// If the operation is part of an application-specified transaction,
217        /// <paramref name="txn"/> is a Transaction object returned from
218        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
219        /// the operation is part of a Berkeley DB Concurrent Data Store group,
220        /// <paramref name="txn"/> is a handle returned from
221        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
222        /// </param>
223        public void Remove(Transaction txn) {
224            Remove(false, txn);
225        }
226        private void Remove(bool NoSync, Transaction txn) {
227            uint flags = NoSync ? DbConstants.DB_TXN_NOSYNC : 0;
228            isOpen = false;
229            seq.remove(Transaction.getDB_TXN(txn), flags);
230        }
231
232        /// <summary>
233        /// Return statistical information for this sequence.
234        /// </summary>
235        /// <returns>Statistical information for this sequence.</returns>
236        public SequenceStats Stats() {
237            return Stats(false);
238        }
239        /// <summary>
240        /// Return statistical information for this sequence.
241        /// </summary>
242        /// <overloads>
243        /// <para>
244        /// In the presence of multiple threads or processes accessing an active
245        /// sequence, the information returned by DB_SEQUENCE->stat() may be
246        /// out-of-date.
247        /// </para>
248		/// <para>
249        /// The DB_SEQUENCE->stat() method cannot be transaction-protected. For
250        /// this reason, it should be called in a thread of control that has no
251        /// open cursors or active transactions.
252        /// </para>
253        /// </overloads>
254        /// <param name="clear">If true, reset statistics.</param>
255        /// <returns>Statistical information for this sequence.</returns>
256        public SequenceStats Stats(bool clear) {
257            uint flags = 0;
258            flags |= clear ? DbConstants.DB_STAT_CLEAR : 0;
259            SequenceStatStruct st = seq.stat(flags);
260            return new SequenceStats(st);
261        }
262
263        /// <summary>
264        /// Release the resources held by this object, and close the sequence if
265        /// it's still open.
266        /// </summary>
267        public void Dispose() {
268            if (isOpen)
269                seq.close(0);
270            seq.Dispose();
271            GC.SuppressFinalize(this);
272        }
273
274        /// <summary>
275        /// The current cache size.
276        /// </summary>
277        public int Cachesize {
278            get {
279                int ret = 0;
280                seq.get_cachesize(ref ret);
281                return ret;
282            }
283        }
284
285        /// <summary>
286        /// The minimum value in the sequence.
287        /// </summary>
288        public Int64 Min {
289            get {
290                Int64 ret = 0;
291                Int64 tmp = 0;
292                seq.get_range(ref ret, ref tmp);
293                return ret;
294            }
295        }
296
297        /// <summary>
298        /// The maximum value in the sequence.
299        /// </summary>
300        public Int64 Max {
301            get {
302                Int64 ret = 0;
303                Int64 tmp = 0;
304                seq.get_range(ref tmp, ref ret);
305                return ret;
306            }
307        }
308
309        /// <summary>
310        /// If true, the sequence should wrap around when it is incremented
311        /// (decremented) past the specified maximum (minimum) value.
312        /// </summary>
313        public bool Wrap {
314            get {
315                uint flags = 0;
316                seq.get_flags(ref flags);
317                return (flags & DbConstants.DB_SEQ_WRAP) != 0;
318            }
319        }
320
321        /// <summary>
322        /// If true, the sequence will be incremented. This is the default.
323        /// </summary>
324        public bool Increment {
325            get {
326                uint flags = 0;
327                seq.get_flags(ref flags);
328                return (flags & DbConstants.DB_SEQ_INC) != 0;
329            }
330        }
331
332        /// <summary>
333        /// If true, the sequence will be decremented.
334        /// </summary>
335        public bool Decrement {
336            get {
337                uint flags = 0;
338                seq.get_flags(ref flags);
339                return (flags & DbConstants.DB_SEQ_DEC) != 0;
340            }
341        }
342    }
343}
344