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.Runtime.InteropServices;
10 using System.Text;
11 using BerkeleyDB.Internal;
12 
13 namespace BerkeleyDB {
14     /// <summary>
15     /// A class representing a SecondaryHashDatabase. The Hash format is an
16     /// extensible, dynamic hashing scheme.
17     /// </summary>
18     public class SecondaryHashDatabase : SecondaryDatabase {
19         private HashFunctionDelegate hashHandler;
20         private EntryComparisonDelegate compareHandler;
21         private EntryComparisonDelegate dupCompareHandler;
22         private BDB_CompareDelegate doCompareRef;
23         private BDB_HashDelegate doHashRef;
24         private BDB_CompareDelegate doDupCompareRef;
25 
26         #region Constructors
SecondaryHashDatabase(DatabaseEnvironment env, uint flags)27         internal SecondaryHashDatabase(DatabaseEnvironment env, uint flags) : base(env, flags) { }
28 
Config(SecondaryHashDatabaseConfig cfg)29         private void Config(SecondaryHashDatabaseConfig cfg) {
30             base.Config(cfg);
31             db.set_flags(cfg.flags);
32 
33             if (cfg.HashFunction != null)
34                 HashFunction = cfg.HashFunction;
35             if (cfg.DuplicateCompare != null)
36                 DupCompare = cfg.DuplicateCompare;
37             if (cfg.fillFactorIsSet)
38                 db.set_h_ffactor(cfg.FillFactor);
39             if (cfg.nelemIsSet)
40                 db.set_h_nelem(cfg.TableSize);
41             if (cfg.Compare != null)
42                 Compare = cfg.Compare;
43         }
44 
45         /// <summary>
46         /// Instantiate a new SecondaryHashDatabase object, open the
47         /// database represented by <paramref name="Filename"/> and associate
48         /// the database with the
49         /// <see cref="SecondaryDatabaseConfig.Primary">primary index</see>.
50         /// </summary>
51         /// <remarks>
52         /// <para>
53         /// If <paramref name="Filename"/> is null, the database is strictly
54         /// temporary and cannot be opened by any other thread of control, thus
55         /// the database can only be accessed by sharing the single database
56         /// object that created it, in circumstances where doing so is safe.
57         /// </para>
58         /// <para>
59         /// If <see cref="DatabaseConfig.AutoCommit"/> is set, the operation
60         /// will be implicitly transaction protected. Note that transactionally
61         /// protected operations on a datbase object requires the object itself
62         /// be transactionally protected during its open.
63         /// </para>
64         /// </remarks>
65         /// <param name="Filename">
66         /// The name of an underlying file that will be used to back the
67         /// database. In-memory databases never intended to be preserved on disk
68         /// may be created by setting this parameter to null.
69         /// </param>
70         /// <param name="cfg">The database's configuration</param>
71         /// <returns>A new, open database object</returns>
Open( string Filename, SecondaryHashDatabaseConfig cfg)72         public static SecondaryHashDatabase Open(
73             string Filename, SecondaryHashDatabaseConfig cfg) {
74             return Open(Filename, null, cfg, null);
75         }
76         /// <summary>
77         /// Instantiate a new SecondaryHashDatabase object, open the
78         /// database represented by <paramref name="Filename"/> and associate
79         /// the database with the
80         /// <see cref="SecondaryDatabaseConfig.Primary">primary index</see>.
81         /// </summary>
82         /// <remarks>
83         /// <para>
84         /// If both <paramref name="Filename"/> and
85         /// <paramref name="DatabaseName"/> are null, the database is strictly
86         /// temporary and cannot be opened by any other thread of control, thus
87         /// the database can only be accessed by sharing the single database
88         /// object that created it, in circumstances where doing so is safe. If
89         /// <paramref name="Filename"/> is null and
90         /// <paramref name="DatabaseName"/> is non-null, the database can be
91         /// opened by other threads of control and will be replicated to client
92         /// sites in any replication group.
93         /// </para>
94         /// <para>
95         /// If <see cref="DatabaseConfig.AutoCommit"/> is set, the operation
96         /// will be implicitly transaction protected. Note that transactionally
97         /// protected operations on a datbase object requires the object itself
98         /// be transactionally protected during its open.
99         /// </para>
100         /// </remarks>
101         /// <param name="Filename">
102         /// The name of an underlying file that will be used to back the
103         /// database. In-memory databases never intended to be preserved on disk
104         /// may be created by setting this parameter to null.
105         /// </param>
106         /// <param name="DatabaseName">
107         /// This parameter allows applications to have multiple databases in a
108         /// single file. Although no DatabaseName needs to be specified, it is
109         /// an error to attempt to open a second database in a file that was not
110         /// initially created using a database name.
111         /// </param>
112         /// <param name="cfg">The database's configuration</param>
113         /// <returns>A new, open database object</returns>
Open(string Filename, string DatabaseName, SecondaryHashDatabaseConfig cfg)114         public static SecondaryHashDatabase Open(string Filename,
115             string DatabaseName, SecondaryHashDatabaseConfig cfg) {
116             return Open(Filename, DatabaseName, cfg, null);
117         }
118         /// <summary>
119         /// Instantiate a new SecondaryHashDatabase object, open the
120         /// database represented by <paramref name="Filename"/> and associate
121         /// the database with the
122         /// <see cref="SecondaryDatabaseConfig.Primary">primary index</see>.
123         /// </summary>
124         /// <remarks>
125         /// <para>
126         /// If <paramref name="Filename"/> is null, the database is strictly
127         /// temporary and cannot be opened by any other thread of control, thus
128         /// the database can only be accessed by sharing the single database
129         /// object that created it, in circumstances where doing so is safe.
130         /// </para>
131         /// <para>
132         /// If <paramref name="txn"/> is null, but
133         /// <see cref="DatabaseConfig.AutoCommit"/> is set, the operation will
134         /// be implicitly transaction protected. Note that transactionally
135         /// protected operations on a datbase object requires the object itself
136         /// be transactionally protected during its open. Also note that the
137         /// transaction must be committed before the object is closed.
138         /// </para>
139         /// </remarks>
140         /// <param name="Filename">
141         /// The name of an underlying file that will be used to back the
142         /// database. In-memory databases never intended to be preserved on disk
143         /// may be created by setting this parameter to null.
144         /// </param>
145         /// <param name="cfg">The database's configuration</param>
146         /// <param name="txn">
147         /// If the operation is part of an application-specified transaction,
148         /// <paramref name="txn"/> is a Transaction object returned from
149         /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
150         /// the operation is part of a Berkeley DB Concurrent Data Store group,
151         /// <paramref name="txn"/> is a handle returned from
152         /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
153         /// </param>
154         /// <returns>A new, open database object</returns>
Open(string Filename, SecondaryHashDatabaseConfig cfg, Transaction txn)155         public static SecondaryHashDatabase Open(string Filename,
156             SecondaryHashDatabaseConfig cfg, Transaction txn) {
157             return Open(Filename, null, cfg, txn);
158         }
159         /// <summary>
160         /// Instantiate a new SecondaryHashDatabase object, open the
161         /// database represented by <paramref name="Filename"/> and associate
162         /// the database with the
163         /// <see cref="SecondaryDatabaseConfig.Primary">primary index</see>.
164         /// </summary>
165         /// <remarks>
166         /// <para>
167         /// If both <paramref name="Filename"/> and
168         /// <paramref name="DatabaseName"/> are null, the database is strictly
169         /// temporary and cannot be opened by any other thread of control, thus
170         /// the database can only be accessed by sharing the single database
171         /// object that created it, in circumstances where doing so is safe. If
172         /// <paramref name="Filename"/> is null and
173         /// <paramref name="DatabaseName"/> is non-null, the database can be
174         /// opened by other threads of control and will be replicated to client
175         /// sites in any replication group.
176         /// </para>
177         /// <para>
178         /// If <paramref name="txn"/> is null, but
179         /// <see cref="DatabaseConfig.AutoCommit"/> is set, the operation will
180         /// be implicitly transaction protected. Note that transactionally
181         /// protected operations on a datbase object requires the object itself
182         /// be transactionally protected during its open. Also note that the
183         /// transaction must be committed before the object is closed.
184         /// </para>
185         /// </remarks>
186         /// <param name="Filename">
187         /// The name of an underlying file that will be used to back the
188         /// database. In-memory databases never intended to be preserved on disk
189         /// may be created by setting this parameter to null.
190         /// </param>
191         /// <param name="DatabaseName">
192         /// This parameter allows applications to have multiple databases in a
193         /// single file. Although no DatabaseName needs to be specified, it is
194         /// an error to attempt to open a second database in a file that was not
195         /// initially created using a database name.
196         /// </param>
197         /// <param name="cfg">The database's configuration</param>
198         /// <param name="txn">
199         /// If the operation is part of an application-specified transaction,
200         /// <paramref name="txn"/> is a Transaction object returned from
201         /// <see cref="DatabaseEnvironment.BeginTransaction"/>; if
202         /// the operation is part of a Berkeley DB Concurrent Data Store group,
203         /// <paramref name="txn"/> is a handle returned from
204         /// <see cref="DatabaseEnvironment.BeginCDSGroup"/>; otherwise null.
205         /// </param>
206         /// <returns>A new, open database object</returns>
Open( string Filename, string DatabaseName, SecondaryHashDatabaseConfig cfg, Transaction txn)207         public static SecondaryHashDatabase Open(
208             string Filename, string DatabaseName,
209             SecondaryHashDatabaseConfig cfg, Transaction txn) {
210             SecondaryHashDatabase ret = new SecondaryHashDatabase(cfg.Env, 0);
211             ret.Config(cfg);
212             ret.db.open(Transaction.getDB_TXN(txn), Filename,
213                 DatabaseName, cfg.DbType.getDBTYPE(), cfg.openFlags, 0);
214             ret.isOpen = true;
215 
216             ret.doAssocRef =
217                 new BDB_AssociateDelegate(SecondaryDatabase.doAssociate);
218             cfg.Primary.db.associate(Transaction.getDB_TXN(txn),
219                 ret.db, ret.doAssocRef, cfg.assocFlags);
220 
221             if (cfg.ForeignKeyDatabase != null) {
222                 if (cfg.OnForeignKeyDelete == ForeignKeyDeleteAction.NULLIFY)
223                     ret.doNullifyRef =
224                         new BDB_AssociateForeignDelegate(doNullify);
225                 else
226                     ret.doNullifyRef = null;
227                 cfg.ForeignKeyDatabase.db.associate_foreign(
228                     ret.db, ret.doNullifyRef, cfg.foreignFlags);
229             }
230             return ret;
231         }
232 
233 
234         #endregion Constructors
235 
236         #region Callbacks
doDupCompare( IntPtr dbp, IntPtr dbt1p, IntPtr dbt2p)237         private static int doDupCompare(
238             IntPtr dbp, IntPtr dbt1p, IntPtr dbt2p) {
239             DB db = new DB(dbp, false);
240             DBT dbt1 = new DBT(dbt1p, false);
241             DBT dbt2 = new DBT(dbt2p, false);
242 
243             SecondaryHashDatabase tmp = (SecondaryHashDatabase)db.api_internal;
244             return tmp.DupCompare(
245                 DatabaseEntry.fromDBT(dbt1), DatabaseEntry.fromDBT(dbt2));
246         }
doHash(IntPtr dbp, IntPtr datap, uint len)247         private static uint doHash(IntPtr dbp, IntPtr datap, uint len) {
248             DB db = new DB(dbp, false);
249             byte[] t_data = new byte[len];
250             Marshal.Copy(datap, t_data, 0, (int)len);
251 
252             SecondaryHashDatabase tmp = (SecondaryHashDatabase)db.api_internal;
253             return tmp.HashFunction(t_data);
254         }
doCompare(IntPtr dbp, IntPtr dbtp1, IntPtr dbtp2)255         private static int doCompare(IntPtr dbp, IntPtr dbtp1, IntPtr dbtp2) {
256             DB db = new DB(dbp, false);
257             DBT dbt1 = new DBT(dbtp1, false);
258             DBT dbt2 = new DBT(dbtp2, false);
259 
260             SecondaryHashDatabase tmp = (SecondaryHashDatabase)db.api_internal;
261             return tmp.Compare(
262                 DatabaseEntry.fromDBT(dbt1), DatabaseEntry.fromDBT(dbt2));
263         }
264         #endregion Callbacks
265 
266         #region Properties
267         /// <summary>
268         /// The secondary Hash key comparison function. The comparison function
269         /// is called whenever it is necessary to compare a key specified by the
270         /// application with a key currently stored in the tree.
271         /// </summary>
272         public EntryComparisonDelegate Compare {
273             get { return compareHandler; }
274             private set {
275                 if (value == null)
276                     db.set_h_compare(null);
277                 else if (compareHandler == null) {
278                     if (doCompareRef == null)
279                         doCompareRef = new BDB_CompareDelegate(doCompare);
280                     db.set_h_compare(doCompareRef);
281                 }
282                 compareHandler = value;
283             }
284         }
285         /// <summary>
286         /// The duplicate data item comparison function.
287         /// </summary>
288         public EntryComparisonDelegate DupCompare {
289             get { return dupCompareHandler; }
290             private set {
291                 /* Cannot be called after open. */
292                 if (value == null)
293                     db.set_dup_compare(null);
294                 else if (dupCompareHandler == null) {
295                     if (doDupCompareRef == null)
296                         doDupCompareRef = new BDB_CompareDelegate(doDupCompare);
297                     db.set_dup_compare(doDupCompareRef);
298                 }
299                 dupCompareHandler = value;
300             }
301         }
302         /// <summary>
303         /// Whether the insertion of duplicate data items in the database is
304         /// permitted, and whether duplicates items are sorted.
305         /// </summary>
306         public DuplicatesPolicy Duplicates {
307             get {
308                 uint flags = 0;
309                 db.get_flags(ref flags);
310                 if ((flags & DbConstants.DB_DUPSORT) != 0)
311                     return DuplicatesPolicy.SORTED;
312                 else if ((flags & DbConstants.DB_DUP) != 0)
313                     return DuplicatesPolicy.UNSORTED;
314                 else
315                     return DuplicatesPolicy.NONE;
316             }
317         }
318         /// <summary>
319         /// The desired density within the hash table.
320         /// </summary>
321         public uint FillFactor {
322             get {
323                 uint ret = 0;
324                 db.get_h_ffactor(ref ret);
325                 return ret;
326             }
327         }
328         /// <summary>
329         /// A user-defined hash function; if no hash function is specified, a
330         /// default hash function is used.
331         /// </summary>
332         public HashFunctionDelegate HashFunction {
333             get { return hashHandler; }
334             private set {
335                 if (value == null)
336                     db.set_h_hash(null);
337                 else if (hashHandler == null) {
338                     if (doHashRef == null)
339                         doHashRef = new BDB_HashDelegate(doHash);
340                     db.set_h_hash(doHashRef);
341                 }
342                 hashHandler = value;
343             }
344         }
345         /// <summary>
346         /// An estimate of the final size of the hash table.
347         /// </summary>
348         public uint TableSize {
349             get {
350                 uint ret = 0;
351                 db.get_h_nelem(ref ret);
352                 return ret;
353             }
354         }
355         #endregion Properties
356     }
357 }
358