/*- * 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 { /// <summary> /// A class representing a secondary Berkeley DB database, a base class for /// access method specific classes. /// </summary> public class SecondaryDatabase : BaseDatabase { private SecondaryKeyGenDelegate keyGenHandler; internal BDB_AssociateDelegate doAssocRef; private ForeignKeyNullifyDelegate nullifierHandler; internal BDB_AssociateForeignDelegate doNullifyRef; #region Constructors /// <summary> /// Protected construtor /// </summary> /// <param name="env">The environment in which to open the DB</param> /// <param name="flags">Flags to pass to DB->create</param> protected SecondaryDatabase(DatabaseEnvironment env, uint flags) : base(env, flags) { } internal static SecondaryDatabase fromDB(DB dbp) { try { return (SecondaryDatabase)dbp.api_internal; } catch { } return null; } /// <summary> /// Protected method to configure the DB. Only valid before DB->open. /// </summary> /// <param name="cfg">Configuration parameters.</param> protected void Config(SecondaryDatabaseConfig cfg) { base.Config(cfg); KeyGen = cfg.KeyGen; Nullifier = cfg.ForeignKeyNullfier; db.set_flags(cfg.flags); } /// <summary> /// Instantiate a new SecondaryDatabase object, open the database /// represented by <paramref name="Filename"/> and associate the /// database with the <see cref="SecondaryDatabaseConfig.Primary"> /// primary index</see>. The file specified by /// <paramref name="Filename"/> must exist. /// </summary> /// <remarks> /// <para> /// If <see cref="DatabaseConfig.AutoCommit"/> 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. /// </para> /// </remarks> /// <param name="Filename"> /// The name of an underlying file that will be used to back the /// database. /// </param> /// <param name="cfg">The database's configuration</param> /// <returns>A new, open database object</returns> public static SecondaryDatabase Open( string Filename, SecondaryDatabaseConfig cfg) { return Open(Filename, null, cfg, null); } /// <summary> /// Instantiate a new SecondaryDatabase object, open the database /// represented by <paramref name="Filename"/> and associate the /// database with the <see cref="SecondaryDatabaseConfig.Primary"> /// primary index</see>. The file specified by /// <paramref name="Filename"/> must exist. /// </summary> /// <remarks> /// <para> /// If <paramref name="Filename"/> is null and /// <paramref name="DatabaseName"/> is non-null, the database can be /// opened by other threads of control and will be replicated to client /// sites in any replication group. /// </para> /// <para> /// If <see cref="DatabaseConfig.AutoCommit"/> 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. /// </para> /// </remarks> /// <param name="Filename"> /// The name of an underlying file that will be used to back the /// database. /// </param> /// <param name="DatabaseName"> /// 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. /// </param> /// <param name="cfg">The database's configuration</param> /// <returns>A new, open database object</returns> public static SecondaryDatabase Open(string Filename, string DatabaseName, SecondaryDatabaseConfig cfg) { return Open(Filename, DatabaseName, cfg, null); } /// <summary> /// Instantiate a new SecondaryDatabase object, open the database /// represented by <paramref name="Filename"/> and associate the /// database with the <see cref="SecondaryDatabaseConfig.Primary"> /// primary index</see>. The file specified by /// <paramref name="Filename"/> must exist. /// </summary> /// <remarks> /// <para> /// If <see cref="DatabaseConfig.AutoCommit"/> 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. /// </para> /// </remarks> /// <param name="Filename"> /// The name of an underlying file that will be used to back the /// database. /// </param> /// <param name="cfg">The database's configuration</param> /// <param name="txn"> /// If the operation is part of an application-specified transaction, /// <paramref name="txn"/> is a Transaction object returned from /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if /// the operation is part of a Berkeley DB Concurrent Data Store group, /// <paramref name="txn"/> is a handle returned from /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null. /// </param> /// <returns>A new, open database object</returns> public static SecondaryDatabase Open(string Filename, SecondaryDatabaseConfig cfg, Transaction txn) { return Open(Filename, null, cfg, txn); } /// <summary> /// Instantiate a new SecondaryDatabase object, open the database /// represented by <paramref name="Filename"/> and associate the /// database with the <see cref="SecondaryDatabaseConfig.Primary"> /// primary index</see>. The file specified by /// <paramref name="Filename"/> must exist. /// </summary> /// <remarks> /// <para> /// If <paramref name="Filename"/> is null and /// <paramref name="DatabaseName"/> is non-null, the database can be /// opened by other threads of control and will be replicated to client /// sites in any replication group. /// </para> /// <para> /// If <see cref="DatabaseConfig.AutoCommit"/> 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. /// </para> /// </remarks> /// <param name="Filename"> /// The name of an underlying file that will be used to back the /// database. /// </param> /// <param name="DatabaseName"> /// 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. /// </param> /// <param name="cfg">The database's configuration</param> /// <param name="txn"> /// If the operation is part of an application-specified transaction, /// <paramref name="txn"/> is a Transaction object returned from /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if /// the operation is part of a Berkeley DB Concurrent Data Store group, /// <paramref name="txn"/> is a handle returned from /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null. /// </param> /// <returns>A new, open database object</returns> public static SecondaryDatabase Open(string Filename, string DatabaseName, SecondaryDatabaseConfig cfg, Transaction txn) { if (cfg.DbType == DatabaseType.BTREE) { return SecondaryBTreeDatabase.Open(Filename, DatabaseName, (SecondaryBTreeDatabaseConfig)cfg, txn); } else if (cfg.DbType == DatabaseType.HASH) { return SecondaryHashDatabase.Open(Filename, DatabaseName, (SecondaryHashDatabaseConfig)cfg, txn); } SecondaryDatabase ret = new SecondaryDatabase(cfg.Env, 0); ret.Config(cfg); ret.db.open(Transaction.getDB_TXN(txn), Filename, DatabaseName, cfg.DbType.getDBTYPE(), cfg.openFlags, 0); ret.doAssocRef = new BDB_AssociateDelegate(doAssociate); cfg.Primary.db.associate(Transaction.getDB_TXN(null), ret.db, ret.doAssocRef, cfg.assocFlags); if (cfg.ForeignKeyDatabase != null) { if (cfg.OnForeignKeyDelete == ForeignKeyDeleteAction.NULLIFY) ret.doNullifyRef = new BDB_AssociateForeignDelegate(doNullify); else ret.doNullifyRef = null; cfg.ForeignKeyDatabase.db.associate_foreign(ret.db, ret.doNullifyRef, cfg.foreignFlags); } return ret; } #endregion Constructors #region Callbacks /// <summary> /// Protected method to call the key generation function. /// </summary> /// <param name="dbp">Secondary DB Handle</param> /// <param name="keyp">Primary Key</param> /// <param name="datap">Primary Data</param> /// <param name="skeyp">Scondary Key</param> /// <returns>0 on success, !0 on failure</returns> protected static int doAssociate( IntPtr dbp, IntPtr keyp, IntPtr datap, IntPtr skeyp) { DB db = new DB(dbp, false); DBT key = new DBT(keyp, false); DBT data = new DBT(datap, false); DBT skey = new DBT(skeyp, false); DatabaseEntry s = ((SecondaryDatabase)db.api_internal).KeyGen( DatabaseEntry.fromDBT(key), DatabaseEntry.fromDBT(data)); if (s == null) return DbConstants.DB_DONOTINDEX; skey.data = s.Data; return 0; } /// <summary> /// Protected method to nullify a foreign key /// </summary> /// <param name="dbp">Secondary DB Handle</param> /// <param name="keyp">Primary Key</param> /// <param name="datap">Primary Data</param> /// <param name="fkeyp">Foreign Key</param> /// <param name="changed">Whether the foreign key has changed</param> /// <returns>0 on success, !0 on failure</returns> protected static int doNullify(IntPtr dbp, IntPtr keyp, IntPtr datap, IntPtr fkeyp, ref int changed) { DB db = new DB(dbp, false); DBT key = new DBT(keyp, false); DBT data = new DBT(datap, false); DBT fkey = new DBT(fkeyp, false); DatabaseEntry d = ((SecondaryDatabase)db.api_internal).Nullifier( DatabaseEntry.fromDBT(key), DatabaseEntry.fromDBT(data), DatabaseEntry.fromDBT(fkey)); if (d == null) changed = 0; else { changed = 1; data.data = d.Data; } return 0; } #endregion Callbacks #region Properties /// <summary> /// The delegate that creates the set of secondary keys corresponding to /// a given primary key and data pair. /// </summary> public SecondaryKeyGenDelegate KeyGen { get { return keyGenHandler; } private set { keyGenHandler = value; } } public ForeignKeyNullifyDelegate Nullifier { get { return nullifierHandler; } private set { nullifierHandler = value; } } #endregion Properties #region Methods /// <summary> /// Create a secondary database cursor. /// </summary> /// <returns>A newly created cursor</returns> public SecondaryCursor SecondaryCursor() { return SecondaryCursor(new CursorConfig(), null); } /// <summary> /// Create a secondary database cursor with the given configuration. /// </summary> /// <param name="cfg"> /// The configuration properties for the cursor. /// </param> /// <returns>A newly created cursor</returns> public SecondaryCursor SecondaryCursor(CursorConfig cfg) { return SecondaryCursor(cfg, null); } /// <summary> /// Create a transactionally protected secondary database cursor. /// </summary> /// <param name="txn"> /// The transaction context in which the cursor may be used. /// </param> /// <returns>A newly created cursor</returns> public SecondaryCursor SecondaryCursor(Transaction txn) { return SecondaryCursor(new CursorConfig(), txn); } /// <summary> /// Create a transactionally protected secondary database cursor with /// the given configuration. /// </summary> /// <param name="cfg"> /// The configuration properties for the cursor. /// </param> /// <param name="txn"> /// The transaction context in which the cursor may be used. /// </param> /// <returns>A newly created cursor</returns> public SecondaryCursor SecondaryCursor( CursorConfig cfg, Transaction txn) { return new SecondaryCursor( db.cursor(Transaction.getDB_TXN(txn), cfg.flags)); } #endregion Methods } }