1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2009, 2013 Oracle and/or its affiliates.  All rights reserved.
5  *
6  */
7 using System;
8 using System.Collections.Generic;
9 using System.Text;
10 using BerkeleyDB.Internal;
11 
12 namespace BerkeleyDB {
13     /// <summary>
14     /// A class representing a QueueDatabase. The Queue format supports fast
15     /// access to fixed-length records accessed sequentially or by logical
16     /// record number.
17     /// </summary>
18     public class QueueDatabase : Database {
19 
20         #region Constructors
QueueDatabase(DatabaseEnvironment env, uint flags)21         private QueueDatabase(DatabaseEnvironment env, uint flags)
22             : base(env, flags) { }
QueueDatabase(BaseDatabase clone)23         internal QueueDatabase(BaseDatabase clone) : base(clone) { }
24 
Config(QueueDatabaseConfig cfg)25         private void Config(QueueDatabaseConfig cfg) {
26             base.Config(cfg);
27             /*
28              * Database.Config calls set_flags, but that doesn't get the Queue
29              * specific flags.  No harm in calling it again.
30              */
31             db.set_flags(cfg.flags);
32 
33             db.set_re_len(cfg.Length);
34 
35             if (cfg.padIsSet)
36                 db.set_re_pad(cfg.PadByte);
37             if (cfg.extentIsSet)
38                 db.set_q_extentsize(cfg.ExtentSize);
39         }
40 
41         /// <summary>
42         /// Instantiate a new QueueDatabase object and open the database
43         /// represented by <paramref name="Filename"/>.
44         /// </summary>
45         /// <remarks>
46         /// <para>
47         /// If <paramref name="Filename"/> is null, the database is strictly
48         /// temporary and cannot be opened by any other thread of control, thus
49         /// the database can only be accessed by sharing the single database
50         /// object that created it, in circumstances where doing so is safe.
51         /// </para>
52         /// <para>
53         /// If <see cref="DatabaseConfig.AutoCommit"/> is set, the operation
54         /// will be implicitly transaction protected. Note that transactionally
55         /// protected operations on a datbase object requires the object itself
56         /// be transactionally protected during its open.
57         /// </para>
58         /// </remarks>
59         /// <param name="Filename">
60         /// The name of an underlying file that will be used to back the
61         /// database. In-memory databases never intended to be preserved on disk
62         /// may be created by setting this parameter to null.
63         /// </param>
64         /// <param name="cfg">The database's configuration</param>
65         /// <returns>A new, open database object</returns>
Open( string Filename, QueueDatabaseConfig cfg)66         public static QueueDatabase Open(
67             string Filename, QueueDatabaseConfig cfg) {
68             return Open(Filename, cfg, null);
69         }
70         /// <summary>
71         /// Instantiate a new QueueDatabase object and open the database
72         /// represented by <paramref name="Filename"/>.
73         /// </summary>
74         /// <remarks>
75         /// <para>
76         /// If <paramref name="Filename"/> is null, the database is strictly
77         /// temporary and cannot be opened by any other thread of control, thus
78         /// the database can only be accessed by sharing the single database
79         /// object that created it, in circumstances where doing so is safe.
80         /// </para>
81         /// <para>
82         /// If <paramref name="txn"/> is null, but
83         /// <see cref="DatabaseConfig.AutoCommit"/> is set, the operation will
84         /// be implicitly transaction protected. Note that transactionally
85         /// protected operations on a datbase object requires the object itself
86         /// be transactionally protected during its open. Also note that the
87         /// transaction must be committed before the object is closed.
88         /// </para>
89         /// </remarks>
90         /// <param name="Filename">
91         /// The name of an underlying file that will be used to back the
92         /// database. In-memory databases never intended to be preserved on disk
93         /// may be created by setting this parameter to null.
94         /// </param>
95         /// <param name="cfg">The database's configuration</param>
96         /// <param name="txn">
97         /// If the operation is part of an application-specified transaction,
98         /// <paramref name="txn"/> is a Transaction object returned from
99         /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
100         /// the operation is part of a Berkeley DB Concurrent Data Store group,
101         /// <paramref name="txn"/> is a handle returned from
102         /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
103         /// </param>
104         /// <returns>A new, open database object</returns>
Open( string Filename, QueueDatabaseConfig cfg, Transaction txn)105         public static QueueDatabase Open(
106             string Filename, QueueDatabaseConfig cfg, Transaction txn) {
107             QueueDatabase ret = new QueueDatabase(cfg.Env, 0);
108             ret.Config(cfg);
109             ret.db.open(Transaction.getDB_TXN(txn),
110                 Filename, null, DBTYPE.DB_QUEUE, cfg.openFlags, 0);
111             ret.isOpen = true;
112             return ret;
113         }
114 
115         #endregion Constructors
116 
117         #region Properties
118         /// <summary>
119         /// The size of the extents used to hold pages in a
120         /// <see cref="QueueDatabase"/>, specified as a number of pages.
121         /// </summary>
122         public uint ExtentSize {
123             get {
124                 uint ret = 0;
125                 db.get_q_extentsize(ref ret);
126                 return ret;
127             }
128         }
129 
130         /// <summary>
131         /// If true, modify the operation of <see cref="QueueDatabase.Consume"/>
132         /// to return key/data pairs in order. That is, they will always return
133         /// the key/data item from the head of the queue.
134         /// </summary>
135         public bool InOrder {
136             get {
137                 uint flags = 0;
138                 db.get_flags(ref flags);
139                 return (flags & DbConstants.DB_INORDER) != 0;
140             }
141         }
142 
143         /// <summary>
144         /// The length of records in the database.
145         /// </summary>
146         public uint Length {
147             get {
148                 uint ret = 0;
149                 db.get_re_len(ref ret);
150                 return ret;
151             }
152         }
153 
154         /// <summary>
155         /// The padding character for short, fixed-length records.
156         /// </summary>
157         public int PadByte {
158             get {
159                 int ret = 0;
160                 db.get_re_pad(ref ret);
161                 return ret;
162             }
163         }
164         #endregion Properties
165 
166         #region Methods
167         /// <summary>
168         /// Append the data item to the end of the database.
169         /// </summary>
170         /// <param name="data">The data item to store in the database</param>
171         /// <returns>The record number allocated to the record</returns>
Append(DatabaseEntry data)172         public uint Append(DatabaseEntry data) {
173             return Append(data, null);
174         }
175         /// <summary>
176         /// Append the data item to the end of the database.
177         /// </summary>
178         /// <remarks>
179         /// There is a minor behavioral difference between
180         /// <see cref="RecnoDatabase.Append"/> and
181         /// <see cref="QueueDatabase.Append"/>. If a transaction enclosing an
182         /// Append operation aborts, the record number may be reallocated in a
183         /// subsequent <see cref="RecnoDatabase.Append"/> operation, but it will
184         /// not be reallocated in a subsequent
185         /// <see cref="QueueDatabase.Append"/> operation.
186         /// </remarks>
187         /// <param name="data">The data item to store in the database</param>
188         /// <param name="txn">
189         /// If the operation is part of an application-specified transaction,
190         /// <paramref name="txn"/> is a Transaction object returned from
191         /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
192         /// the operation is part of a Berkeley DB Concurrent Data Store group,
193         /// <paramref name="txn"/> is a handle returned from
194         /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
195         /// </param>
196         /// <returns>The record number allocated to the record</returns>
Append(DatabaseEntry data, Transaction txn)197         public uint Append(DatabaseEntry data, Transaction txn) {
198             DatabaseEntry key = new DatabaseEntry();
199             Put(key, data, txn, DbConstants.DB_APPEND);
200             return BitConverter.ToUInt32(key.Data, 0);
201         }
202 
203         /// <summary>
204         /// Return the record number and data from the available record closest
205         /// to the head of the queue, and delete the record.
206         /// </summary>
207         /// <param name="wait">
208         /// If true and the Queue database is empty, the thread of control will
209         /// wait until there is data in the queue before returning.
210         /// </param>
211         /// <exception cref="LockNotGrantedException">
212         /// If lock or transaction timeouts have been specified, a
213         /// <see cref="LockNotGrantedException"/> may be thrown. This failure,
214         /// by itself, does not require the enclosing transaction be aborted.
215         /// </exception>
216         /// <returns>
217         /// A <see cref="KeyValuePair{T,T}"/> whose Key
218         /// parameter is the record number and whose Value parameter is the
219         /// retrieved data.
220         /// </returns>
Consume(bool wait)221         public KeyValuePair<uint, DatabaseEntry> Consume(bool wait) {
222             return Consume(wait, null, null);
223         }
224         /// <summary>
225         /// Return the record number and data from the available record closest
226         /// to the head of the queue, and delete the record.
227         /// </summary>
228         /// <param name="wait">
229         /// If true and the Queue database is empty, the thread of control will
230         /// wait until there is data in the queue before returning.
231         /// </param>
232         /// <param name="txn">
233         /// <paramref name="txn"/> is a Transaction object returned from
234         /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
235         /// the operation is part of a Berkeley DB Concurrent Data Store group,
236         /// <paramref name="txn"/> is a handle returned from
237         /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
238         /// </param>
239         /// <exception cref="LockNotGrantedException">
240         /// If lock or transaction timeouts have been specified, a
241         /// <see cref="LockNotGrantedException"/> may be thrown. This failure,
242         /// by itself, does not require the enclosing transaction be aborted.
243         /// </exception>
244         /// <returns>
245         /// A <see cref="KeyValuePair{T,T}"/> whose Key
246         /// parameter is the record number and whose Value parameter is the
247         /// retrieved data.
248         /// </returns>
Consume( bool wait, Transaction txn)249         public KeyValuePair<uint, DatabaseEntry> Consume(
250             bool wait, Transaction txn) {
251             return Consume(wait, txn, null);
252         }
253         /// <summary>
254         /// Return the record number and data from the available record closest
255         /// to the head of the queue, and delete the record.
256         /// </summary>
257         /// <param name="wait">
258         /// If true and the Queue database is empty, the thread of control will
259         /// wait until there is data in the queue before returning.
260         /// </param>
261         /// <param name="txn">
262         /// <paramref name="txn"/> is a Transaction object returned from
263         /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
264         /// the operation is part of a Berkeley DB Concurrent Data Store group,
265         /// <paramref name="txn"/> is a handle returned from
266         /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
267         /// </param>
268         /// <param name="info">The locking behavior to use.</param>
269         /// <exception cref="LockNotGrantedException">
270         /// If lock or transaction timeouts have been specified, a
271         /// <see cref="LockNotGrantedException"/> may be thrown. This failure,
272         /// by itself, does not require the enclosing transaction be aborted.
273         /// </exception>
274         /// <returns>
275         /// A <see cref="KeyValuePair{T,T}"/> whose Key
276         /// parameter is the record number and whose Value parameter is the
277         /// retrieved data.
278         /// </returns>
Consume( bool wait, Transaction txn, LockingInfo info)279         public KeyValuePair<uint, DatabaseEntry> Consume(
280             bool wait, Transaction txn, LockingInfo info) {
281             KeyValuePair<DatabaseEntry, DatabaseEntry> record;
282 
283             record = Get(null, null, txn, info,
284                 wait ? DbConstants.DB_CONSUME_WAIT : DbConstants.DB_CONSUME);
285             return new KeyValuePair<uint, DatabaseEntry>(
286                 BitConverter.ToUInt32(record.Key.Data, 0), record.Value);
287 
288         }
289 
290         /// <summary>
291         /// Return the database statistical information which does not require
292         /// traversal of the database.
293         /// </summary>
294         /// <returns>
295         /// The database statistical information which does not require
296         /// traversal of the database.
297         /// </returns>
FastStats()298         public QueueStats FastStats() {
299             return Stats(null, true, Isolation.DEGREE_THREE);
300         }
301         /// <summary>
302         /// Return the database statistical information which does not require
303         /// traversal of the database.
304         /// </summary>
305         /// <param name="txn">
306         /// If the operation is part of an application-specified transaction,
307         /// <paramref name="txn"/> is a Transaction object returned from
308         /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
309         /// the operation is part of a Berkeley DB Concurrent Data Store group,
310         /// <paramref name="txn"/> is a handle returned from
311         /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
312         /// </param>
313         /// <returns>
314         /// The database statistical information which does not require
315         /// traversal of the database.
316         /// </returns>
FastStats(Transaction txn)317         public QueueStats FastStats(Transaction txn) {
318             return Stats(txn, true, Isolation.DEGREE_THREE);
319         }
320         /// <summary>
321         /// Return the database statistical information which does not require
322         /// traversal of the database.
323         /// </summary>
324         /// <overloads>
325         /// <para>
326         /// Among other things, this method makes it possible for applications
327         /// to request key and record counts without incurring the performance
328         /// penalty of traversing the entire database.
329         /// </para>
330         /// <para>
331         /// The statistical information is described by the
332         /// <see cref="BTreeStats"/>, <see cref="HashStats"/>,
333         /// <see cref="QueueStats"/>, and <see cref="RecnoStats"/> classes.
334         /// </para>
335         /// </overloads>
336         /// <param name="txn">
337         /// If the operation is part of an application-specified transaction,
338         /// <paramref name="txn"/> is a Transaction object returned from
339         /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
340         /// the operation is part of a Berkeley DB Concurrent Data Store group,
341         /// <paramref name="txn"/> is a handle returned from
342         /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
343         /// </param>
344         /// <param name="isoDegree">
345         /// The level of isolation for database reads.
346         /// <see cref="Isolation.DEGREE_ONE"/> will be silently ignored for
347         /// databases which did not specify
348         /// <see cref="DatabaseConfig.ReadUncommitted"/>.
349         /// </param>
350         /// <returns>
351         /// The database statistical information which does not require
352         /// traversal of the database.
353         /// </returns>
FastStats(Transaction txn, Isolation isoDegree)354         public QueueStats FastStats(Transaction txn, Isolation isoDegree) {
355             return Stats(txn, true, isoDegree);
356         }
357 
358         /// <summary>
359         /// Return the database statistical information for this database.
360         /// </summary>
361         /// <returns>Database statistical information.</returns>
Stats()362         public QueueStats Stats() {
363             return Stats(null, false, Isolation.DEGREE_THREE);
364         }
365         /// <summary>
366         /// Return the database statistical information for this database.
367         /// </summary>
368         /// <param name="txn">
369         /// If the operation is part of an application-specified transaction,
370         /// <paramref name="txn"/> is a Transaction object returned from
371         /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
372         /// the operation is part of a Berkeley DB Concurrent Data Store group,
373         /// <paramref name="txn"/> is a handle returned from
374         /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
375         /// </param>
376         /// <returns>Database statistical information.</returns>
Stats(Transaction txn)377         public QueueStats Stats(Transaction txn) {
378             return Stats(txn, false, Isolation.DEGREE_THREE);
379         }
380         /// <summary>
381         /// Return the database statistical information for this database.
382         /// </summary>
383         /// <overloads>
384         /// The statistical information is described by
385         /// <see cref="BTreeStats"/>.
386         /// </overloads>
387         /// <param name="txn">
388         /// If the operation is part of an application-specified transaction,
389         /// <paramref name="txn"/> is a Transaction object returned from
390         /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
391         /// the operation is part of a Berkeley DB Concurrent Data Store group,
392         /// <paramref name="txn"/> is a handle returned from
393         /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
394         /// </param>
395         /// <param name="isoDegree">
396         /// The level of isolation for database reads.
397         /// <see cref="Isolation.DEGREE_ONE"/> will be silently ignored for
398         /// databases which did not specify
399         /// <see cref="DatabaseConfig.ReadUncommitted"/>.
400         /// </param>
401         /// <returns>Database statistical information.</returns>
Stats(Transaction txn, Isolation isoDegree)402         public QueueStats Stats(Transaction txn, Isolation isoDegree) {
403             return Stats(txn, false, isoDegree);
404         }
Stats(Transaction txn, bool fast, Isolation isoDegree)405         private QueueStats Stats(Transaction txn, bool fast, Isolation isoDegree) {
406             uint flags = 0;
407             flags |= fast ? DbConstants.DB_FAST_STAT : 0;
408             switch (isoDegree) {
409                 case Isolation.DEGREE_ONE:
410                     flags |= DbConstants.DB_READ_UNCOMMITTED;
411                     break;
412                 case Isolation.DEGREE_TWO:
413                     flags |= DbConstants.DB_READ_COMMITTED;
414                     break;
415             }
416             QueueStatStruct st = db.stat_qam(Transaction.getDB_TXN(txn), flags);
417             return new QueueStats(st);
418         }
419         #endregion Methods
420     }
421 }
422