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.Runtime.InteropServices;
10using System.Text;
11using BerkeleyDB.Internal;
12
13namespace BerkeleyDB {
14    /// <summary>
15    /// A class representing a BTreeDatabase.  The Btree format is a
16    /// representation of a sorted, balanced tree structure.
17    /// </summary>
18    public class BTreeDatabase : Database {
19        private BTreeCompressDelegate compressHandler;
20        private BTreeDecompressDelegate decompressHandler;
21        private EntryComparisonDelegate compareHandler, prefixCompareHandler;
22        private EntryComparisonDelegate dupCompareHandler;
23        private BDB_CompareDelegate doCompareRef;
24        private BDB_CompareDelegate doPrefixCompareRef;
25        private BDB_CompareDelegate doDupCompareRef;
26        private BDB_CompressDelegate doCompressRef;
27        private BDB_DecompressDelegate doDecompressRef;
28
29        #region Constructors
30        private BTreeDatabase(DatabaseEnvironment env, uint flags)
31            : base(env, flags) { }
32        internal BTreeDatabase(BaseDatabase clone) : base(clone) { }
33
34        private void Config(BTreeDatabaseConfig cfg) {
35            base.Config(cfg);
36            /*
37             * Database.Config calls set_flags, but that doesn't get the BTree
38             * specific flags.  No harm in calling it again.
39             */
40            db.set_flags(cfg.flags);
41
42            if (cfg.BTreeCompare != null)
43                Compare = cfg.BTreeCompare;
44
45            if (cfg.BTreePrefixCompare != null)
46                PrefixCompare = cfg.BTreePrefixCompare;
47
48            // The duplicate comparison function cannot change.
49            if (cfg.DuplicateCompare != null)
50                DupCompare = cfg.DuplicateCompare;
51
52            if (cfg.minkeysIsSet)
53                db.set_bt_minkey(cfg.MinKeysPerPage);
54
55            if (cfg.compressionIsSet) {
56                Compress = cfg.Compress;
57                Decompress = cfg.Decompress;
58                if (Compress == null)
59                    doCompressRef = null;
60                else
61                    doCompressRef = new BDB_CompressDelegate(doCompress);
62                if (Decompress == null)
63                    doDecompressRef = null;
64                else
65                    doDecompressRef = new BDB_DecompressDelegate(doDecompress);
66                db.set_bt_compress(doCompressRef, doDecompressRef);
67            }
68        }
69
70        /// <summary>
71        /// Instantiate a new BTreeDatabase 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 <see cref="DatabaseConfig.AutoCommit"/> is set, the operation
83        /// will be implicitly transaction protected. Note that transactionally
84        /// protected operations on a datbase object requires the object itself
85        /// be transactionally protected during its open.
86        /// </para>
87        /// </remarks>
88        /// <param name="Filename">
89        /// The name of an underlying file that will be used to back the
90        /// database. In-memory databases never intended to be preserved on disk
91        /// may be created by setting this parameter to null.
92        /// </param>
93        /// <param name="cfg">The database's configuration</param>
94        /// <returns>A new, open database object</returns>
95        public static BTreeDatabase Open(
96            string Filename, BTreeDatabaseConfig cfg) {
97            return Open(Filename, null, cfg, null);
98        }
99        /// <summary>
100        /// Instantiate a new BTreeDatabase object and open the database
101        /// represented by <paramref name="Filename"/> and
102        /// <paramref name="DatabaseName"/>.
103        /// </summary>
104        /// <remarks>
105        /// <para>
106        /// If both <paramref name="Filename"/> and
107        /// <paramref name="DatabaseName"/> are null, the database is strictly
108        /// temporary and cannot be opened by any other thread of control, thus
109        /// the database can only be accessed by sharing the single database
110        /// object that created it, in circumstances where doing so is safe. If
111        /// <paramref name="Filename"/> is null and
112        /// <paramref name="DatabaseName"/> is non-null, the database can be
113        /// opened by other threads of control and will be replicated to client
114        /// sites in any replication group.
115        /// </para>
116        /// <para>
117        /// If <see cref="DatabaseConfig.AutoCommit"/> is set, the operation
118        /// will be implicitly transaction protected. Note that transactionally
119        /// protected operations on a datbase object requires the object itself
120        /// be transactionally protected during its open.
121        /// </para>
122        /// </remarks>
123        /// <param name="Filename">
124        /// The name of an underlying file that will be used to back the
125        /// database. In-memory databases never intended to be preserved on disk
126        /// may be created by setting this parameter to null.
127        /// </param>
128        /// <param name="DatabaseName">
129        /// This parameter allows applications to have multiple databases in a
130        /// single file. Although no DatabaseName needs to be specified, it is
131        /// an error to attempt to open a second database in a file that was not
132        /// initially created using a database name.
133        /// </param>
134        /// <param name="cfg">The database's configuration</param>
135        /// <returns>A new, open database object</returns>
136        public static BTreeDatabase Open(
137            string Filename, string DatabaseName, BTreeDatabaseConfig cfg) {
138            return Open(Filename, DatabaseName, cfg, null);
139        }
140        /// <summary>
141        /// Instantiate a new BTreeDatabase object and open the database
142        /// represented by <paramref name="Filename"/>.
143        /// </summary>
144        /// <remarks>
145        /// <para>
146        /// If <paramref name="Filename"/> is null, the database is strictly
147        /// temporary and cannot be opened by any other thread of control, thus
148        /// the database can only be accessed by sharing the single database
149        /// object that created it, in circumstances where doing so is safe.
150        /// </para>
151        /// <para>
152        /// If <paramref name="txn"/> is null, but
153        /// <see cref="DatabaseConfig.AutoCommit"/> is set, the operation will
154        /// be implicitly transaction protected. Note that transactionally
155        /// protected operations on a datbase object requires the object itself
156        /// be transactionally protected during its open. Also note that the
157        /// transaction must be committed before the object is closed.
158        /// </para>
159        /// </remarks>
160        /// <param name="Filename">
161        /// The name of an underlying file that will be used to back the
162        /// database. In-memory databases never intended to be preserved on disk
163        /// may be created by setting this parameter to null.
164        /// </param>
165        /// <param name="cfg">The database's configuration</param>
166        /// <param name="txn">
167        /// If the operation is part of an application-specified transaction,
168        /// <paramref name="txn"/> is a Transaction object returned from
169        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
170        /// the operation is part of a Berkeley DB Concurrent Data Store group,
171        /// <paramref name="txn"/> is a handle returned from
172        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
173        /// </param>
174        /// <returns>A new, open database object</returns>
175        public static BTreeDatabase Open(
176            string Filename, BTreeDatabaseConfig cfg, Transaction txn) {
177            return Open(Filename, null, cfg, txn);
178        }
179        /// <summary>
180        /// Instantiate a new BTreeDatabase object and open the database
181        /// represented by <paramref name="Filename"/> and
182        /// <paramref name="DatabaseName"/>.
183        /// </summary>
184        /// <remarks>
185        /// <para>
186        /// If both <paramref name="Filename"/> and
187        /// <paramref name="DatabaseName"/> are null, the database is strictly
188        /// temporary and cannot be opened by any other thread of control, thus
189        /// the database can only be accessed by sharing the single database
190        /// object that created it, in circumstances where doing so is safe. If
191        /// <paramref name="Filename"/> is null and
192        /// <paramref name="DatabaseName"/> is non-null, the database can be
193        /// opened by other threads of control and will be replicated to client
194        /// sites in any replication group.
195        /// </para>
196        /// <para>
197        /// If <paramref name="txn"/> is null, but
198        /// <see cref="DatabaseConfig.AutoCommit"/> is set, the operation will
199        /// be implicitly transaction protected. Note that transactionally
200        /// protected operations on a datbase object requires the object itself
201        /// be transactionally protected during its open. Also note that the
202        /// transaction must be committed before the object is closed.
203        /// </para>
204        /// </remarks>
205        /// <param name="Filename">
206        /// The name of an underlying file that will be used to back the
207        /// database. In-memory databases never intended to be preserved on disk
208        /// may be created by setting this parameter to null.
209        /// </param>
210        /// <param name="DatabaseName">
211        /// This parameter allows applications to have multiple databases in a
212        /// single file. Although no DatabaseName needs to be specified, it is
213        /// an error to attempt to open a second database in a file that was not
214        /// initially created using a database name.
215        /// </param>
216        /// <param name="cfg">The database's configuration</param>
217        /// <param name="txn">
218        /// If the operation is part of an application-specified transaction,
219        /// <paramref name="txn"/> is a Transaction object returned from
220        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
221        /// the operation is part of a Berkeley DB Concurrent Data Store group,
222        /// <paramref name="txn"/> is a handle returned from
223        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
224        /// </param>
225        /// <returns>A new, open database object</returns>
226        public static BTreeDatabase Open(string Filename,
227            string DatabaseName, BTreeDatabaseConfig cfg, Transaction txn) {
228            BTreeDatabase ret = new BTreeDatabase(cfg.Env, 0);
229            ret.Config(cfg);
230            ret.db.open(Transaction.getDB_TXN(txn),
231                Filename, DatabaseName, DBTYPE.DB_BTREE, cfg.openFlags, 0);
232            ret.isOpen = true;
233            return ret;
234        }
235        #endregion Constructors
236
237        #region Callbacks
238        private static int doCompare(IntPtr dbp, IntPtr dbtp1, IntPtr dbtp2) {
239            DB db = new DB(dbp, false);
240            DBT dbt1 = new DBT(dbtp1, false);
241            DBT dbt2 = new DBT(dbtp2, false);
242            BTreeDatabase btdb = (BTreeDatabase)(db.api_internal);
243            return btdb.Compare(
244                DatabaseEntry.fromDBT(dbt1), DatabaseEntry.fromDBT(dbt2));
245        }
246        private static int doCompress(IntPtr dbp, IntPtr prevKeyp,
247            IntPtr prevDatap, IntPtr keyp, IntPtr datap, IntPtr destp) {
248            DB db = new DB(dbp, false);
249            DatabaseEntry prevKey =
250                DatabaseEntry.fromDBT(new DBT(prevKeyp, false));
251            DatabaseEntry prevData =
252                DatabaseEntry.fromDBT(new DBT(prevDatap, false));
253            DatabaseEntry key = DatabaseEntry.fromDBT(new DBT(keyp, false));
254            DatabaseEntry data = DatabaseEntry.fromDBT(new DBT(datap, false));
255            DBT dest = new DBT(destp, false);
256            BTreeDatabase btdb = (BTreeDatabase)(db.api_internal);
257            byte[] arr = new byte[(int)dest.ulen];
258            int len;
259            try {
260                if (btdb.Compress(prevKey, prevData, key, data, ref arr, out len)) {
261                    Marshal.Copy(arr, 0, dest.dataPtr, len);
262                    dest.size = (uint)len;
263                    return 0;
264                } else {
265                    return DbConstants.DB_BUFFER_SMALL;
266                }
267            } catch (Exception) {
268                return -1;
269            }
270        }
271        private static int doDecompress(IntPtr dbp, IntPtr prevKeyp,
272            IntPtr prevDatap, IntPtr cmpp, IntPtr destKeyp, IntPtr destDatap) {
273            DB db = new DB(dbp, false);
274            DatabaseEntry prevKey =
275                DatabaseEntry.fromDBT(new DBT(prevKeyp, false));
276            DatabaseEntry prevData =
277                DatabaseEntry.fromDBT(new DBT(prevDatap, false));
278            DBT compressed = new DBT(cmpp, false);
279            DBT destKey = new DBT(destKeyp, false);
280            DBT destData = new DBT(destDatap, false);
281            BTreeDatabase btdb = (BTreeDatabase)(db.api_internal);
282            uint size;
283            try {
284                KeyValuePair<DatabaseEntry, DatabaseEntry> kvp = btdb.Decompress(prevKey, prevData, compressed.data, out size);
285                int keylen = kvp.Key.Data.Length;
286                int datalen = kvp.Value.Data.Length;
287                destKey.size = (uint)keylen;
288                destData.size = (uint)datalen;
289                if (keylen > destKey.ulen ||
290                    datalen > destData.ulen)
291                    return DbConstants.DB_BUFFER_SMALL;
292                Marshal.Copy(kvp.Key.Data, 0, destKey.dataPtr, keylen);
293                Marshal.Copy(kvp.Value.Data, 0, destData.dataPtr, datalen);
294                compressed.size = size;
295                return 0;
296            } catch (Exception) {
297                return -1;
298            }
299        }
300        private static int doDupCompare(
301            IntPtr dbp, IntPtr dbt1p, IntPtr dbt2p) {
302            DB db = new DB(dbp, false);
303            DBT dbt1 = new DBT(dbt1p, false);
304            DBT dbt2 = new DBT(dbt2p, false);
305
306            BTreeDatabase btdb = (BTreeDatabase)(db.api_internal);
307            return btdb.DupCompare(
308                DatabaseEntry.fromDBT(dbt1), DatabaseEntry.fromDBT(dbt2));
309        }
310        private static int doPrefixCompare(
311            IntPtr dbp, IntPtr dbtp1, IntPtr dbtp2) {
312            DB db = new DB(dbp, false);
313            DBT dbt1 = new DBT(dbtp1, false);
314            DBT dbt2 = new DBT(dbtp2, false);
315            BTreeDatabase btdb = (BTreeDatabase)(db.api_internal);
316            return btdb.PrefixCompare(
317                DatabaseEntry.fromDBT(dbt1), DatabaseEntry.fromDBT(dbt2));
318        }
319        #endregion Callbacks
320
321        #region Properties
322        // Sorted alpha by property name
323        /// <summary>
324        /// The Btree key comparison function. The comparison function is called
325        /// whenever it is necessary to compare a key specified by the
326        /// application with a key currently stored in the tree.
327        /// </summary>
328        public EntryComparisonDelegate Compare {
329            get { return compareHandler; }
330            private set {
331                if (value == null)
332                    db.set_bt_compare(null);
333                else if (compareHandler == null) {
334                    if (doCompareRef == null)
335                        doCompareRef = new BDB_CompareDelegate(doCompare);
336                    db.set_bt_compare(doCompareRef);
337                }
338                compareHandler = value;
339            }
340        }
341
342        /// <summary>
343        /// The compression function used to store key/data pairs in the
344        /// database.
345        /// </summary>
346        public BTreeCompressDelegate Compress {
347            get { return compressHandler; }
348            private set { compressHandler = value; }
349        }
350
351        /// <summary>
352        /// The decompression function used to retrieve key/data pairs from the
353        /// database.
354        /// </summary>
355        public BTreeDecompressDelegate Decompress {
356            get { return decompressHandler; }
357            private set { decompressHandler = value; }
358        }
359
360        /// <summary>
361        /// The duplicate data item comparison function.
362        /// </summary>
363        public EntryComparisonDelegate DupCompare {
364            get { return dupCompareHandler; }
365            private set {
366                /* Cannot be called after open. */
367                if (value == null)
368                    db.set_dup_compare(null);
369                else if (dupCompareHandler == null) {
370                    if (doDupCompareRef == null)
371                        doDupCompareRef = new BDB_CompareDelegate(doDupCompare);
372                    db.set_dup_compare(doDupCompareRef);
373                }
374                dupCompareHandler = value;
375            }
376        }
377        /// <summary>
378        /// Whether the insertion of duplicate data items in the database is
379        /// permitted, and whether duplicates items are sorted.
380        /// </summary>
381        public DuplicatesPolicy Duplicates {
382            get {
383                uint flags = 0;
384                db.get_flags(ref flags);
385                if ((flags & DbConstants.DB_DUPSORT) != 0)
386                    return DuplicatesPolicy.SORTED;
387                else if ((flags & DbConstants.DB_DUP) != 0)
388                    return DuplicatesPolicy.UNSORTED;
389                else
390                    return DuplicatesPolicy.NONE;
391            }
392        }
393
394        /// <summary>
395        /// The minimum number of key/data pairs intended to be stored on any
396        /// single Btree leaf page.
397        /// </summary>
398        public uint MinKeysPerPage {
399            get {
400                uint ret = 0;
401                db.get_bt_minkey(ref ret);
402                return ret;
403            }
404        }
405
406        /// <summary>
407        /// The Btree prefix function. The prefix function is used to determine
408        /// the amount by which keys stored on the Btree internal pages can be
409        /// safely truncated without losing their uniqueness.
410        /// </summary>
411        public EntryComparisonDelegate PrefixCompare {
412            get { return prefixCompareHandler; }
413            private set {
414                if (value == null)
415                    db.set_bt_prefix(null);
416                else if (prefixCompareHandler == null) {
417                    if (doPrefixCompareRef == null)
418                        doPrefixCompareRef =
419            new BDB_CompareDelegate(doPrefixCompare);
420                    db.set_bt_prefix(doPrefixCompareRef);
421                }
422                prefixCompareHandler = value;
423            }
424        }
425
426        /// <summary>
427        /// If true, this object supports retrieval from the Btree using record
428        /// numbers.
429        /// </summary>
430        public bool RecordNumbers {
431            get {
432                uint flags = 0;
433                db.get_flags(ref flags);
434                return (flags & DbConstants.DB_RECNUM) != 0;
435            }
436        }
437
438        /// <summary>
439        /// If false, empty pages will not be coalesced into higher-level pages.
440        /// </summary>
441        public bool ReverseSplit {
442            get {
443                uint flags = 0;
444                db.get_flags(ref flags);
445                return (flags & DbConstants.DB_REVSPLITOFF) == 0;
446            }
447        }
448
449        #endregion Properties
450
451        #region Methods
452        // Sorted alpha by method name
453
454        /// <summary>
455        /// Compact the database, and optionally return unused database pages to
456        /// the underlying filesystem.
457        /// </summary>
458        /// <remarks>
459        /// If the operation occurs in a transactional database, the operation
460        /// will be implicitly transaction protected using multiple
461        /// transactions. These transactions will be periodically committed to
462        /// avoid locking large sections of the tree. Any deadlocks encountered
463        /// cause the compaction operation to be retried from the point of the
464        /// last transaction commit.
465        /// </remarks>
466        /// <param name="cdata">Compact configuration parameters</param>
467        /// <returns>Compact operation statistics</returns>
468        public CompactData Compact(CompactConfig cdata) {
469            return Compact(cdata, null);
470        }
471        /// <summary>
472        /// Compact the database, and optionally return unused database pages to
473        /// the underlying filesystem.
474        /// </summary>
475        /// <remarks>
476        /// <para>
477        /// If <paramref name="txn"/> is non-null, then the operation is
478        /// performed using that transaction. In this event, large sections of
479        /// the tree may be locked during the course of the transaction.
480        /// </para>
481        /// <para>
482        /// If <paramref name="txn"/> is null, but the operation occurs in a
483        /// transactional database, the operation will be implicitly transaction
484        /// protected using multiple transactions. These transactions will be
485        /// periodically committed to avoid locking large sections of the tree.
486        /// Any deadlocks encountered cause the compaction operation to be
487        /// retried from the point of the last transaction commit.
488        /// </para>
489        /// </remarks>
490        /// <param name="cdata">Compact configuration parameters</param>
491        /// <param name="txn">
492        /// If the operation is part of an application-specified transaction,
493        /// <paramref name="txn"/> is a Transaction object returned from
494        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
495        /// the operation is part of a Berkeley DB Concurrent Data Store group,
496        /// <paramref name="txn"/> is a handle returned from
497        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
498        /// </param>
499        /// <returns>Compact operation statistics</returns>
500        public CompactData Compact(CompactConfig cdata, Transaction txn) {
501            DatabaseEntry end = null;
502            if (cdata.returnEnd)
503                end = new DatabaseEntry();
504
505            db.compact(Transaction.getDB_TXN(txn), cdata.start, cdata.stop,
506                CompactConfig.getDB_COMPACT(cdata), cdata.flags, end);
507            return new CompactData(CompactConfig.getDB_COMPACT(cdata), end);
508        }
509
510        /// <summary>
511        /// Create a database cursor.
512        /// </summary>
513        /// <returns>A newly created cursor</returns>
514        public new BTreeCursor Cursor() {
515            return Cursor(new CursorConfig(), null);
516        }
517        /// <summary>
518        /// Create a database cursor with the given configuration.
519        /// </summary>
520        /// <param name="cfg">
521        /// The configuration properties for the cursor.
522        /// </param>
523        /// <returns>A newly created cursor</returns>
524        public new BTreeCursor Cursor(CursorConfig cfg) {
525            return Cursor(cfg, null);
526        }
527        /// <summary>
528        /// Create a transactionally protected database cursor.
529        /// </summary>
530        /// <param name="txn">
531        /// The transaction context in which the cursor may be used.
532        /// </param>
533        /// <returns>A newly created cursor</returns>
534        public new BTreeCursor Cursor(Transaction txn) {
535            return Cursor(new CursorConfig(), txn);
536        }
537        /// <summary>
538        /// Create a transactionally protected database cursor with the given
539        /// configuration.
540        /// </summary>
541        /// <param name="cfg">
542        /// The configuration properties for the cursor.
543        /// </param>
544        /// <param name="txn">
545        /// The transaction context in which the cursor may be used.
546        /// </param>
547        /// <returns>A newly created cursor</returns>
548        public new BTreeCursor Cursor(CursorConfig cfg, Transaction txn) {
549            return new BTreeCursor(
550                db.cursor(Transaction.getDB_TXN(txn), cfg.flags), Pagesize);
551        }
552
553        /// <summary>
554        /// Return the database statistical information which does not require
555        /// traversal of the database.
556        /// </summary>
557        /// <returns>
558        /// The database statistical information which does not require
559        /// traversal of the database.
560        /// </returns>
561        public BTreeStats FastStats() {
562            return Stats(null, true, Isolation.DEGREE_THREE);
563        }
564        /// <summary>
565        /// Return the database statistical information which does not require
566        /// traversal of the database.
567        /// </summary>
568        /// <param name="txn">
569        /// If the operation is part of an application-specified transaction,
570        /// <paramref name="txn"/> is a Transaction object returned from
571        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
572        /// the operation is part of a Berkeley DB Concurrent Data Store group,
573        /// <paramref name="txn"/> is a handle returned from
574        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
575        /// </param>
576        /// <returns>
577        /// The database statistical information which does not require
578        /// traversal of the database.
579        /// </returns>
580        public BTreeStats FastStats(Transaction txn) {
581            return Stats(txn, true, Isolation.DEGREE_THREE);
582        }
583        /// <summary>
584        /// Return the database statistical information which does not require
585        /// traversal of the database.
586        /// </summary>
587        /// <overloads>
588        /// <para>
589        /// Among other things, this method makes it possible for applications
590        /// to request key and record counts without incurring the performance
591        /// penalty of traversing the entire database.
592        /// </para>
593        /// <para>
594        /// The statistical information is described by the
595        /// <see cref="BTreeStats"/>, <see cref="HashStats"/>,
596        /// <see cref="QueueStats"/>, and <see cref="RecnoStats"/> classes.
597        /// </para>
598        /// </overloads>
599        /// <param name="txn">
600        /// If the operation is part of an application-specified transaction,
601        /// <paramref name="txn"/> is a Transaction object returned from
602        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
603        /// the operation is part of a Berkeley DB Concurrent Data Store group,
604        /// <paramref name="txn"/> is a handle returned from
605        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
606        /// </param>
607        /// <param name="isoDegree">
608        /// The level of isolation for database reads.
609        /// <see cref="Isolation.DEGREE_ONE"/> will be silently ignored for
610        /// databases which did not specify
611        /// <see cref="DatabaseConfig.ReadUncommitted"/>.
612        /// </param>
613        /// <returns>
614        /// The database statistical information which does not require
615        /// traversal of the database.
616        /// </returns>
617        public BTreeStats FastStats(Transaction txn, Isolation isoDegree) {
618            return Stats(txn, true, isoDegree);
619        }
620
621        /// <summary>
622        /// Retrieve a specific numbered key/data pair from the database.
623        /// </summary>
624        /// <param name="recno">
625        /// The record number of the record to be retrieved.
626        /// </param>
627        /// <returns>
628        /// A <see cref="KeyValuePair{T,T}"/> whose Key
629        /// parameter is <paramref name="key"/> and whose Value parameter is the
630        /// retrieved data.
631        /// </returns>
632        public KeyValuePair<DatabaseEntry, DatabaseEntry> Get(uint recno) {
633            return Get(recno, null, null);
634        }
635        /// <summary>
636        /// Retrieve a specific numbered key/data pair from the database.
637        /// </summary>
638        /// <param name="recno">
639        /// The record number of the record to be retrieved.
640        /// </param>
641        /// <param name="txn">
642        /// <paramref name="txn"/> is a Transaction object returned from
643        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
644        /// the operation is part of a Berkeley DB Concurrent Data Store group,
645        /// <paramref name="txn"/> is a handle returned from
646        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
647        /// </param>
648        /// <returns>
649        /// A <see cref="KeyValuePair{T,T}"/> whose Key
650        /// parameter is <paramref name="key"/> and whose Value parameter is the
651        /// retrieved data.
652        /// </returns>
653        public KeyValuePair<DatabaseEntry, DatabaseEntry> Get(
654            uint recno, Transaction txn) {
655            return Get(recno, txn, null);
656        }
657        /// <summary>
658        /// Retrieve a specific numbered key/data pair from the database.
659        /// </summary>
660        /// <param name="recno">
661        /// The record number of the record to be retrieved.
662        /// </param>
663        /// <param name="txn">
664        /// <paramref name="txn"/> is a Transaction object returned from
665        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
666        /// the operation is part of a Berkeley DB Concurrent Data Store group,
667        /// <paramref name="txn"/> is a handle returned from
668        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
669        /// </param>
670        /// <param name="info">The locking behavior to use.</param>
671        /// <returns>
672        /// A <see cref="KeyValuePair{T,T}"/> whose Key
673        /// parameter is <paramref name="key"/> and whose Value parameter is the
674        /// retrieved data.
675        /// </returns>
676        public KeyValuePair<DatabaseEntry, DatabaseEntry> Get(
677            uint recno, Transaction txn, LockingInfo info) {
678            DatabaseEntry key = new DatabaseEntry();
679            key.Data = BitConverter.GetBytes(recno);
680
681            return Get(key, null, txn, info, DbConstants.DB_SET_RECNO);
682        }
683
684        public KeyValuePair<DatabaseEntry, MultipleDatabaseEntry> GetMultiple(
685            uint recno) {
686            return GetMultiple(recno, (int)Pagesize, null, null);
687        }
688        public KeyValuePair<DatabaseEntry, MultipleDatabaseEntry> GetMultiple(
689            uint recno, int BufferSize) {
690            return GetMultiple(recno, BufferSize, null, null);
691        }
692        public KeyValuePair<DatabaseEntry, MultipleDatabaseEntry> GetMultiple(
693            uint recno, int BufferSize, Transaction txn) {
694            return GetMultiple(recno, BufferSize, txn, null);
695        }
696        public KeyValuePair<DatabaseEntry, MultipleDatabaseEntry> GetMultiple(
697            uint recno, int BufferSize, Transaction txn, LockingInfo info) {
698            KeyValuePair<DatabaseEntry, DatabaseEntry> kvp;
699
700            DatabaseEntry key = new DatabaseEntry();
701            key.Data = BitConverter.GetBytes(recno);
702
703            DatabaseEntry data = new DatabaseEntry();
704
705            for (; ; ) {
706                data.UserData = new byte[BufferSize];
707                try {
708                    kvp = Get(key, data, txn, info,
709                        DbConstants.DB_MULTIPLE | DbConstants.DB_SET_RECNO);
710                    break;
711                } catch (MemoryException) {
712                    int sz = (int)data.size;
713                    if (sz > BufferSize)
714                        BufferSize = sz;
715                    else
716                        BufferSize *= 2;
717                }
718            }
719            MultipleDatabaseEntry dbe = new MultipleDatabaseEntry(kvp.Value);
720            return new KeyValuePair<DatabaseEntry, MultipleDatabaseEntry>(
721                kvp.Key, dbe);
722        }
723
724        /// <summary>
725        /// Return an estimate of the proportion of keys that are less than,
726        /// equal to, and greater than the specified key.
727        /// </summary>
728        /// <param name="key">The key to search for</param>
729        /// <returns>
730        /// An estimate of the proportion of keys that are less than, equal to,
731        /// and greater than the specified key.
732        /// </returns>
733        public KeyRange KeyRange(DatabaseEntry key) {
734            return KeyRange(key, null);
735        }
736        /// <summary>
737        /// Return an estimate of the proportion of keys that are less than,
738        /// equal to, and greater than the specified key.
739        /// </summary>
740        /// <param name="key">The key to search for</param>
741        /// <param name="txn">
742        /// If the operation is part of an application-specified transaction,
743        /// <paramref name="txn"/> is a Transaction object returned from
744        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
745        /// the operation is part of a Berkeley DB Concurrent Data Store group,
746        /// <paramref name="txn"/> is a handle returned from
747        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
748        /// </param>
749        /// <returns>
750        /// An estimate of the proportion of keys that are less than, equal to,
751        /// and greater than the specified key.
752        /// </returns>
753        public KeyRange KeyRange(DatabaseEntry key, Transaction txn) {
754            DB_KEY_RANGE range = new DB_KEY_RANGE();
755            db.key_range(Transaction.getDB_TXN(txn), key, range, 0);
756            return new KeyRange(range);
757        }
758
759        /// <summary>
760        /// Store the key/data pair in the database only if it does not already
761        /// appear in the database.
762        /// </summary>
763        /// <param name="key">The key to store in the database</param>
764        /// <param name="data">The data item to store in the database</param>
765        public void PutNoDuplicate(DatabaseEntry key, DatabaseEntry data) {
766            PutNoDuplicate(key, data, null);
767        }
768        /// <summary>
769        /// Store the key/data pair in the database only if it does not already
770        /// appear in the database.
771        /// </summary>
772        /// <param name="key">The key to store in the database</param>
773        /// <param name="data">The data item to store in the database</param>
774        /// <param name="txn">
775        /// If the operation is part of an application-specified transaction,
776        /// <paramref name="txn"/> is a Transaction object returned from
777        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
778        /// the operation is part of a Berkeley DB Concurrent Data Store group,
779        /// <paramref name="txn"/> is a handle returned from
780        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
781        /// </param>
782        public void PutNoDuplicate(
783            DatabaseEntry key, DatabaseEntry data, Transaction txn) {
784            Put(key, data, txn, DbConstants.DB_NODUPDATA);
785        }
786
787        /// <summary>
788        /// Return the database statistical information for this database.
789        /// </summary>
790        /// <returns>Database statistical information.</returns>
791        public BTreeStats Stats() {
792            return Stats(null, false, Isolation.DEGREE_THREE);
793        }
794        /// <summary>
795        /// Return the database statistical information for this database.
796        /// </summary>
797        /// <param name="txn">
798        /// If the operation is part of an application-specified transaction,
799        /// <paramref name="txn"/> is a Transaction object returned from
800        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
801        /// the operation is part of a Berkeley DB Concurrent Data Store group,
802        /// <paramref name="txn"/> is a handle returned from
803        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
804        /// </param>
805        /// <returns>Database statistical information.</returns>
806        public BTreeStats Stats(Transaction txn) {
807            return Stats(txn, false, Isolation.DEGREE_THREE);
808        }
809        /// <summary>
810        /// Return the database statistical information for this database.
811        /// </summary>
812        /// <overloads>
813        /// The statistical information is described by
814        /// <see cref="BTreeStats"/>.
815        /// </overloads>
816        /// <param name="txn">
817        /// If the operation is part of an application-specified transaction,
818        /// <paramref name="txn"/> is a Transaction object returned from
819        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
820        /// the operation is part of a Berkeley DB Concurrent Data Store group,
821        /// <paramref name="txn"/> is a handle returned from
822        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
823        /// </param>
824        /// <param name="isoDegree">
825        /// The level of isolation for database reads.
826        /// <see cref="Isolation.DEGREE_ONE"/> will be silently ignored for
827        /// databases which did not specify
828        /// <see cref="DatabaseConfig.ReadUncommitted"/>.
829        /// </param>
830        /// <returns>Database statistical information.</returns>
831        public BTreeStats Stats(Transaction txn, Isolation isoDegree) {
832            return Stats(txn, false, isoDegree);
833        }
834        private BTreeStats Stats(
835            Transaction txn, bool fast, Isolation isoDegree) {
836            uint flags = 0;
837            flags |= fast ? DbConstants.DB_FAST_STAT : 0;
838            switch (isoDegree) {
839                case Isolation.DEGREE_ONE:
840                    flags |= DbConstants.DB_READ_UNCOMMITTED;
841                    break;
842                case Isolation.DEGREE_TWO:
843                    flags |= DbConstants.DB_READ_COMMITTED;
844                    break;
845            }
846            BTreeStatStruct st = db.stat_bt(Transaction.getDB_TXN(txn), flags);
847            return new BTreeStats(st);
848        }
849
850        /// <summary>
851        /// Return pages to the filesystem that are already free and at the end
852        /// of the file.
853        /// </summary>
854        /// <returns>
855        /// The number of database pages returned to the filesystem
856        /// </returns>
857        public uint TruncateUnusedPages() {
858            return TruncateUnusedPages(null);
859        }
860        /// <summary>
861        /// Return pages to the filesystem that are already free and at the end
862        /// of the file.
863        /// </summary>
864        /// <param name="txn">
865        /// If the operation is part of an application-specified transaction,
866        /// <paramref name="txn"/> is a Transaction object returned from
867        /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
868        /// the operation is part of a Berkeley DB Concurrent Data Store group,
869        /// <paramref name="txn"/> is a handle returned from
870        /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
871        /// </param>
872        /// <returns>
873        /// The number of database pages returned to the filesystem
874        /// </returns>
875        public uint TruncateUnusedPages(Transaction txn) {
876            DB_COMPACT cdata = new DB_COMPACT();
877            db.compact(Transaction.getDB_TXN(txn),
878                null, null, cdata, DbConstants.DB_FREELIST_ONLY, null);
879            return cdata.compact_pages_truncated;
880        }
881
882        #endregion Methods
883    }
884}
885