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