/*-
* 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
}
}