/*- * 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 { /// /// A class representing a RecnoDatabase. The Recno format supports fixed- /// or variable-length records, accessed sequentially or by logical record /// number, and optionally backed by a flat text file. /// public class RecnoDatabase : Database { private BDB_AppendRecnoDelegate doAppendRef; private AppendRecordDelegate appendHandler; #region Constructors private RecnoDatabase(DatabaseEnvironment env, uint flags) : base(env, flags) { } internal RecnoDatabase(BaseDatabase clone) : base(clone) { } private void Config(RecnoDatabaseConfig cfg) { base.Config(cfg); /* * Database.Config calls set_flags, but that doesn't get the Recno * specific flags. No harm in calling it again. */ db.set_flags(cfg.flags); if (cfg.delimiterIsSet) RecordDelimiter = cfg.Delimiter; if (cfg.lengthIsSet) RecordLength = cfg.Length; if (cfg.padIsSet) RecordPad = cfg.PadByte; if (cfg.BackingFile != null) SourceFile = cfg.BackingFile; } /// /// Instantiate a new RecnoDatabase object and open the database /// represented by . /// /// /// /// If is null, the database is strictly /// temporary and cannot be opened by any other thread of control, thus /// the database can only be accessed by sharing the single database /// object that created it, in circumstances where doing so is safe. /// /// /// If is set, the operation /// will be implicitly transaction protected. Note that transactionally /// protected operations on a datbase object requires the object itself /// be transactionally protected during its open. /// /// /// /// The name of an underlying file that will be used to back the /// database. In-memory databases never intended to be preserved on disk /// may be created by setting this parameter to null. /// /// The database's configuration /// A new, open database object public static RecnoDatabase Open( string Filename, RecnoDatabaseConfig cfg) { return Open(Filename, null, cfg, null); } /// /// Instantiate a new RecnoDatabase object and open the database /// represented by and /// . /// /// /// /// If both and /// are null, the database is strictly /// temporary and cannot be opened by any other thread of control, thus /// the database can only be accessed by sharing the single database /// object that created it, in circumstances where doing so is safe. If /// is null and /// is non-null, the database can be /// opened by other threads of control and will be replicated to client /// sites in any replication group. /// /// /// If is set, the operation /// will be implicitly transaction protected. Note that transactionally /// protected operations on a datbase object requires the object itself /// be transactionally protected during its open. /// /// /// /// The name of an underlying file that will be used to back the /// database. In-memory databases never intended to be preserved on disk /// may be created by setting this parameter to null. /// /// /// This parameter allows applications to have multiple databases in a /// single file. Although no DatabaseName needs to be specified, it is /// an error to attempt to open a second database in a file that was not /// initially created using a database name. /// /// The database's configuration /// A new, open database object public static RecnoDatabase Open( string Filename, string DatabaseName, RecnoDatabaseConfig cfg) { return Open(Filename, DatabaseName, cfg, null); } /// /// Instantiate a new RecnoDatabase object and open the database /// represented by . /// /// /// /// If is null, the database is strictly /// temporary and cannot be opened by any other thread of control, thus /// the database can only be accessed by sharing the single database /// object that created it, in circumstances where doing so is safe. /// /// /// If is null, but /// is set, the operation will /// be implicitly transaction protected. Note that transactionally /// protected operations on a datbase object requires the object itself /// be transactionally protected during its open. Also note that the /// transaction must be committed before the object is closed. /// /// /// /// The name of an underlying file that will be used to back the /// database. In-memory databases never intended to be preserved on disk /// may be created by setting this parameter to null. /// /// The database's configuration /// /// If the operation is part of an application-specified transaction, /// is a Transaction object returned from /// ; if /// the operation is part of a Berkeley DB Concurrent Data Store group, /// is a handle returned from /// ; otherwise null. /// /// A new, open database object public static RecnoDatabase Open( string Filename, RecnoDatabaseConfig cfg, Transaction txn) { return Open(Filename, null, cfg, txn); } /// /// Instantiate a new RecnoDatabase object and open the database /// represented by and /// . /// /// /// /// If both and /// are null, the database is strictly /// temporary and cannot be opened by any other thread of control, thus /// the database can only be accessed by sharing the single database /// object that created it, in circumstances where doing so is safe. If /// is null and /// is non-null, the database can be /// opened by other threads of control and will be replicated to client /// sites in any replication group. /// /// /// If is null, but /// is set, the operation will /// be implicitly transaction protected. Note that transactionally /// protected operations on a datbase object requires the object itself /// be transactionally protected during its open. Also note that the /// transaction must be committed before the object is closed. /// /// /// /// The name of an underlying file that will be used to back the /// database. In-memory databases never intended to be preserved on disk /// may be created by setting this parameter to null. /// /// /// This parameter allows applications to have multiple databases in a /// single file. Although no DatabaseName needs to be specified, it is /// an error to attempt to open a second database in a file that was not /// initially created using a database name. /// /// The database's configuration /// /// If the operation is part of an application-specified transaction, /// is a Transaction object returned from /// ; if /// the operation is part of a Berkeley DB Concurrent Data Store group, /// is a handle returned from /// ; otherwise null. /// /// A new, open database object public static RecnoDatabase Open(string Filename, string DatabaseName, RecnoDatabaseConfig cfg, Transaction txn) { RecnoDatabase ret = new RecnoDatabase(cfg.Env, 0); ret.Config(cfg); ret.db.open(Transaction.getDB_TXN(txn), Filename, DatabaseName, DBTYPE.DB_RECNO, cfg.openFlags, 0); ret.isOpen = true; return ret; } #endregion Constructors #region Callbacks private static void doAppend(IntPtr dbp, IntPtr dbtp1, uint recno) { DB db = new DB(dbp, false); DBT dbt1 = new DBT(dbtp1, false); RecnoDatabase rdb = (RecnoDatabase)(db.api_internal); rdb.AppendCallback(DatabaseEntry.fromDBT(dbt1), recno); } #endregion Callbacks #region Properties /// /// A function to call after the record number has been selected but /// before the data has been stored into the database. /// /// /// /// When using , it may be useful to /// modify the stored data based on the generated key. If a delegate is /// specified, it will be called after the record number has been /// selected, but before the data has been stored. /// /// public AppendRecordDelegate AppendCallback { get { return appendHandler; } set { if (value == null) db.set_append_recno(null); else if (appendHandler == null) { if (doAppendRef == null) doAppendRef = new BDB_AppendRecnoDelegate(doAppend); db.set_append_recno(doAppendRef); } appendHandler = value; } } /// /// The delimiting byte used to mark the end of a record in /// . /// public int RecordDelimiter { get { int ret = 0; db.get_re_delim(ref ret); return ret; } private set { db.set_re_delim(value); } } /// /// If using fixed-length, not byte-delimited records, the length of the /// records. /// public uint RecordLength { get { uint ret = 0; db.get_re_len(ref ret); return ret; } private set { db.set_re_len(value); } } /// /// The padding character for short, fixed-length records. /// public int RecordPad { get { int ret = 0; db.get_re_pad(ref ret); return ret; } private set { db.set_re_pad(value); } } /// /// If true, the logical record numbers are mutable, and change as /// records are added to and deleted from the database. /// public bool Renumber { get { uint flags = 0; db.get_flags(ref flags); return (flags & DbConstants.DB_RENUMBER) != 0; } } /// /// If true, any file will be read in its /// entirety when is called. If false, /// may be read lazily. /// public bool Snapshot { get { uint flags = 0; db.get_flags(ref flags); return (flags & DbConstants.DB_SNAPSHOT) != 0; } } /// /// The underlying source file for the Recno access method. /// public string SourceFile { get { string ret = ""; db.get_re_source(ref ret); return ret; } private set { db.set_re_source(value); } } #endregion Properties #region Methods /// /// Append the data item to the end of the database. /// /// The data item to store in the database /// The record number allocated to the record public uint Append(DatabaseEntry data) { return Append(data, null); } /// /// Append the data item to the end of the database. /// /// /// There is a minor behavioral difference between /// and /// . If a transaction enclosing an /// Append operation aborts, the record number may be reallocated in a /// subsequent operation, but it will /// not be reallocated in a subsequent /// operation. /// /// The data item to store in the database /// /// If the operation is part of an application-specified transaction, /// is a Transaction object returned from /// ; if /// the operation is part of a Berkeley DB Concurrent Data Store group, /// is a handle returned from /// ; otherwise null. /// /// The record number allocated to the record public uint Append(DatabaseEntry data, Transaction txn) { DatabaseEntry key = new DatabaseEntry(); Put(key, data, txn, DbConstants.DB_APPEND); return BitConverter.ToUInt32(key.Data, 0); } /// /// Compact the database, and optionally return unused database pages to /// the underlying filesystem. /// /// /// If the operation occurs in a transactional database, the operation /// will be implicitly transaction protected using multiple /// transactions. These transactions will be periodically committed to /// avoid locking large sections of the tree. Any deadlocks encountered /// cause the compaction operation to be retried from the point of the /// last transaction commit. /// /// Compact configuration parameters /// Compact operation statistics public CompactData Compact(CompactConfig cdata) { return Compact(cdata, null); } /// /// Compact the database, and optionally return unused database pages to /// the underlying filesystem. /// /// /// /// If is non-null, then the operation is /// performed using that transaction. In this event, large sections of /// the tree may be locked during the course of the transaction. /// /// /// If is null, but the operation occurs in a /// transactional database, the operation will be implicitly transaction /// protected using multiple transactions. These transactions will be /// periodically committed to avoid locking large sections of the tree. /// Any deadlocks encountered cause the compaction operation to be /// retried from the point of the last transaction commit. /// /// /// Compact configuration parameters /// /// If the operation is part of an application-specified transaction, /// is a Transaction object returned from /// ; if /// the operation is part of a Berkeley DB Concurrent Data Store group, /// is a handle returned from /// ; otherwise null. /// /// Compact operation statistics public CompactData Compact(CompactConfig cdata, Transaction txn) { DatabaseEntry end = null; if (cdata.returnEnd) end = new DatabaseEntry(); db.compact(Transaction.getDB_TXN(txn), cdata.start, cdata.stop, CompactConfig.getDB_COMPACT(cdata), cdata.flags, end); return new CompactData(CompactConfig.getDB_COMPACT(cdata), end); } /// /// Create a database cursor. /// /// A newly created cursor public new RecnoCursor Cursor() { return Cursor(new CursorConfig(), null); } /// /// Create a database cursor with the given configuration. /// /// /// The configuration properties for the cursor. /// /// A newly created cursor public new RecnoCursor Cursor(CursorConfig cfg) { return Cursor(cfg, null); } /// /// Create a transactionally protected database cursor. /// /// /// The transaction context in which the cursor may be used. /// /// A newly created cursor public new RecnoCursor Cursor(Transaction txn) { return Cursor(new CursorConfig(), txn); } /// /// Create a transactionally protected database cursor with the given /// configuration. /// /// /// The configuration properties for the cursor. /// /// /// The transaction context in which the cursor may be used. /// /// A newly created cursor public new RecnoCursor Cursor(CursorConfig cfg, Transaction txn) { return new RecnoCursor( db.cursor(Transaction.getDB_TXN(txn), cfg.flags), Pagesize); } /// /// Return the database statistical information which does not require /// traversal of the database. /// /// /// The database statistical information which does not require /// traversal of the database. /// public RecnoStats FastStats() { return Stats(null, true, Isolation.DEGREE_THREE); } /// /// Return the database statistical information which does not require /// traversal of the database. /// /// /// If the operation is part of an application-specified transaction, /// is a Transaction object returned from /// ; if /// the operation is part of a Berkeley DB Concurrent Data Store group, /// is a handle returned from /// ; otherwise null. /// /// /// The database statistical information which does not require /// traversal of the database. /// public RecnoStats FastStats(Transaction txn) { return Stats(txn, true, Isolation.DEGREE_THREE); } /// /// Return the database statistical information which does not require /// traversal of the database. /// /// /// /// Among other things, this method makes it possible for applications /// to request key and record counts without incurring the performance /// penalty of traversing the entire database. /// /// /// The statistical information is described by the /// , , /// , and classes. /// /// /// /// If the operation is part of an application-specified transaction, /// is a Transaction object returned from /// ; if /// the operation is part of a Berkeley DB Concurrent Data Store group, /// is a handle returned from /// ; otherwise null. /// /// /// The level of isolation for database reads. /// will be silently ignored for /// databases which did not specify /// . /// /// /// The database statistical information which does not require /// traversal of the database. /// public RecnoStats FastStats(Transaction txn, Isolation isoDegree) { return Stats(txn, true, isoDegree); } /// /// Return the database statistical information for this database. /// /// Database statistical information. public RecnoStats Stats() { return Stats(null, false, Isolation.DEGREE_THREE); } /// /// Return the database statistical information for this database. /// /// /// If the operation is part of an application-specified transaction, /// is a Transaction object returned from /// ; if /// the operation is part of a Berkeley DB Concurrent Data Store group, /// is a handle returned from /// ; otherwise null. /// /// Database statistical information. public RecnoStats Stats(Transaction txn) { return Stats(txn, false, Isolation.DEGREE_THREE); } /// /// Return the database statistical information for this database. /// /// /// The statistical information is described by /// . /// /// /// If the operation is part of an application-specified transaction, /// is a Transaction object returned from /// ; if /// the operation is part of a Berkeley DB Concurrent Data Store group, /// is a handle returned from /// ; otherwise null. /// /// /// The level of isolation for database reads. /// will be silently ignored for /// databases which did not specify /// . /// /// Database statistical information. public RecnoStats Stats(Transaction txn, Isolation isoDegree) { return Stats(txn, false, isoDegree); } private RecnoStats Stats( Transaction txn, bool fast, Isolation isoDegree) { uint flags = 0; flags |= fast ? DbConstants.DB_FAST_STAT : 0; switch (isoDegree) { case Isolation.DEGREE_ONE: flags |= DbConstants.DB_READ_UNCOMMITTED; break; case Isolation.DEGREE_TWO: flags |= DbConstants.DB_READ_COMMITTED; break; } BTreeStatStruct st = db.stat_bt(Transaction.getDB_TXN(txn), flags); return new RecnoStats(st); } /// /// Return pages to the filesystem that are already free and at the end /// of the file. /// /// /// The number of database pages returned to the filesystem /// public uint TruncateUnusedPages() { return TruncateUnusedPages(null); } /// /// Return pages to the filesystem that are already free and at the end /// of the file. /// /// /// If the operation is part of an application-specified transaction, /// is a Transaction object returned from /// ; if /// the operation is part of a Berkeley DB Concurrent Data Store group, /// is a handle returned from /// ; otherwise null. /// /// /// The number of database pages returned to the filesystem /// public uint TruncateUnusedPages(Transaction txn) { DB_COMPACT cdata = new DB_COMPACT(); db.compact(Transaction.getDB_TXN(txn), null, null, cdata, DbConstants.DB_FREELIST_ONLY, null); return cdata.compact_pages_truncated; } #endregion Methods } }