1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002,2008 Oracle. All rights reserved. 5 * 6 * $Id: SecondaryDatabase.java,v 12.7 2008/01/17 05:04:53 mjc Exp $ 7 */ 8 9package com.sleepycat.db; 10 11import com.sleepycat.db.internal.Db; 12import com.sleepycat.db.internal.DbConstants; 13 14/** 15A secondary database handle. 16<p> 17Secondary databases are opened with {@link 18Environment#openSecondaryDatabase Environment.openSecondaryDatabase} and are 19always associated with a single primary database. The distinguishing 20characteristics of a secondary database are: 21<ul> 22<li>Records are automatically added to a secondary database when records are 23added, modified and deleted in the primary database. Direct calls to 24<code>put()</code> methods on a secondary database are prohibited.</li> 25<li>The {@link #delete delete} method of a secondary database will delete 26the primary record and as well as all its associated secondary records.</li> 27<li>Calls to all <code>get()</code> methods will return the data from the 28associated primary database.</li> 29<li>Additional <code>get()</code> method signatures are provided to return 30the primary key in an additional <code>pKey</code> parameter.</li> 31<li>Calls to {@link #openCursor openCursor} will return a {@link 32SecondaryCursor}, which itself has <code>get()</code> methods that return 33the data of the primary database and additional <code>get()</code> method 34signatures for returning the primary key.</li> 35<li>The {@link #openSecondaryCursor openSecondaryCursor} method is provided 36to return a {@link SecondaryCursor} that doesn't require casting.</li> 37</ul> 38<p> 39Before opening or creating a secondary database you must implement the {@link 40SecondaryKeyCreator} 41interface. 42<p> 43For example, to create a secondary database that supports duplicates: 44<pre> 45 Database primaryDb; // The primary database must already be open. 46 SecondaryKeyCreator keyCreator; // Your key creator implementation. 47 SecondaryConfig secConfig = new SecondaryConfig(); 48 secConfig.setAllowCreate(true); 49 secConfig.setSortedDuplicates(true); 50 secConfig.setKeyCreator(keyCreator); 51 SecondaryDatabase newDb = env.openSecondaryDatabase(transaction, 52 "myDatabaseName", 53 primaryDb, 54 secConfig) 55</pre> 56<p> 57If a primary database is to be associated with one or more secondary 58databases, it may not be configured for duplicates. 59<p> 60Note that the associations between primary and secondary databases are not 61stored persistently. Whenever a primary database is opened for write access by 62the application, the appropriate associated secondary databases should also be 63opened by the application. This is necessary to ensure data integrity when 64changes are made to the primary database. 65*/ 66public class SecondaryDatabase extends Database { 67 private final Database primaryDatabase; 68 69 /* package */ 70 SecondaryDatabase(final Db db, final Database primaryDatabase) 71 throws DatabaseException { 72 73 super(db); 74 this.primaryDatabase = primaryDatabase; 75 } 76 77 /** 78 Open a database. 79<p> 80The database is represented by the file and database parameters. 81<p> 82The currently supported database file formats (or <em>access 83methods</em>) are Btree, Hash, Queue, and Recno. The Btree format is a 84representation of a sorted, balanced tree structure. The Hash format 85is an extensible, dynamic hashing scheme. The Queue format supports 86fast access to fixed-length records accessed sequentially or by logical 87record number. The Recno format supports fixed- or variable-length 88records, accessed sequentially or by logical record number, and 89optionally backed by a flat text file. 90<p> 91Storage and retrieval are based on key/data pairs; see {@link com.sleepycat.db.DatabaseEntry DatabaseEntry} 92for more information. 93<p> 94Opening a database is a relatively expensive operation, and maintaining 95a set of open databases will normally be preferable to repeatedly 96opening and closing the database for each new query. 97<p> 98In-memory databases never intended to be preserved on disk may be 99created by setting both the fileName and databaseName parameters to 100null. Note that in-memory databases can only ever be shared by sharing 101the single database handle that created them, in circumstances where 102doing so is safe. The environment variable <code>TMPDIR</code> may 103be used as a directory in which to create temporary backing files. 104<p> 105@param fileName 106The name of an underlying file that will be used to back the database. 107On Windows platforms, this argument will be interpreted as a UTF-8 108string, which is equivalent to ASCII for Latin characters. 109<p> 110@param databaseName 111An optional parameter that allows applications to have multiple 112databases in a single file. Although no databaseName parameter needs 113to be specified, it is an error to attempt to open a second database in 114a physical file that was not initially created using a databaseName 115parameter. Further, the databaseName parameter is not supported by the 116Queue format. 117<p> 118@param primaryDatabase 119a database handle for the primary database that is to be indexed. 120<p> 121@param config The secondary database open attributes. If null, default attributes are used. 122 */ 123 public SecondaryDatabase(final String fileName, 124 final String databaseName, 125 final Database primaryDatabase, 126 final SecondaryConfig config) 127 throws DatabaseException, java.io.FileNotFoundException { 128 129 this(SecondaryConfig.checkNull(config).openSecondaryDatabase( 130 null, null, fileName, databaseName, primaryDatabase.db), 131 primaryDatabase); 132 } 133 134 /** {@inheritDoc} */ 135 public Cursor openCursor(final Transaction txn, final CursorConfig config) 136 throws DatabaseException { 137 138 return openSecondaryCursor(txn, config); 139 } 140 141 /** 142 Obtain a cursor on a database, returning a <code>SecondaryCursor</code>. 143 Calling this method is the equivalent of calling {@link #openCursor} and 144 casting the result to {@link SecondaryCursor}. 145 <p> 146 @param txn 147 To use a cursor for writing to a transactional database, an explicit 148 transaction must be specified. For read-only access to a transactional 149 database, the transaction may be null. For a non-transactional database, 150 the transaction must be null. 151 <p> 152 To transaction-protect cursor operations, cursors must be opened and closed 153 within the context of a transaction, and the txn parameter specifies the 154 transaction context in which the cursor will be used. 155 <p> 156 @param config 157 The cursor attributes. If null, default attributes are used. 158 <p> 159 @return 160 A secondary database cursor. 161 <p> 162 @throws DatabaseException if a failure occurs. 163 */ 164 public SecondaryCursor openSecondaryCursor(final Transaction txn, 165 final CursorConfig config) 166 throws DatabaseException { 167 168 return new SecondaryCursor(this, 169 CursorConfig.checkNull(config).openCursor(db, 170 (txn == null) ? null : txn.txn), config); 171 } 172 173 /** 174 Returns the primary database associated with this secondary database. 175 <p> 176 @return the primary database associated with this secondary database. 177 */ 178 public Database getPrimaryDatabase() { 179 return primaryDatabase; 180 } 181 182 /** {@inheritDoc} */ 183 public DatabaseConfig getConfig() 184 throws DatabaseException { 185 186 return getSecondaryConfig(); 187 } 188 189 /** 190 Returns a copy of the secondary configuration of this database. 191 <p> 192 @return a copy of the secondary configuration of this database. 193 <p> 194 <p> 195@throws DatabaseException if a failure occurs. 196 */ 197 public SecondaryConfig getSecondaryConfig() 198 throws DatabaseException { 199 200 return new SecondaryConfig(db); 201 } 202 203 /** 204 Retrieves the key/data pair with the given key. If the matching key has 205duplicate values, the first data item in the set of duplicates is returned. 206Retrieval of duplicates requires the use of {@link Cursor} operations. 207<p> 208@param txn 209For a transactional database, an explicit transaction may be specified to 210transaction-protect the operation, or null may be specified to perform the 211operation without transaction protection. For a non-transactional database, 212null must be specified. 213<p> 214@param key the secondary key 215used as input. It must be initialized with a non-null byte array by the 216caller. 217<p> 218@param pKey the primary key 219returned as output. Its byte array does not need to be initialized by the 220caller. 221<p> 222@param data the primary data 223returned as output. Its byte array does not need to be initialized by the 224caller. 225<p> 226@param lockMode the locking attributes; if null, default attributes are used. 227<p> 228@return {@link com.sleepycat.db.OperationStatus#NOTFOUND OperationStatus.NOTFOUND} if no matching key/data pair is 229found; {@link com.sleepycat.db.OperationStatus#KEYEMPTY OperationStatus.KEYEMPTY} if the database is a Queue or Recno database and the specified key exists, but was never explicitly created by the application or was later deleted; otherwise, {@link com.sleepycat.db.OperationStatus#SUCCESS OperationStatus.SUCCESS}. 230<p> 231<p> 232@throws DeadlockException if the operation was selected to resolve a 233deadlock. 234<p> 235@throws IllegalArgumentException if an invalid parameter was specified. 236<p> 237@throws DatabaseException if a failure occurs. 238 */ 239 public OperationStatus get(final Transaction txn, 240 final DatabaseEntry key, 241 final DatabaseEntry pKey, 242 final DatabaseEntry data, 243 final LockMode lockMode) 244 throws DatabaseException { 245 246 return OperationStatus.fromInt( 247 db.pget((txn == null) ? null : txn.txn, key, pKey, data, 248 LockMode.getFlag(lockMode) | 249 ((data == null) ? 0 : data.getMultiFlag()))); 250 } 251 252 /** 253 Retrieves the key/data pair with the specified secondary and primary key, that 254is, both the primary and secondary key items must match. 255<p> 256@param txn 257For a transactional database, an explicit transaction may be specified to 258transaction-protect the operation, or null may be specified to perform the 259operation without transaction protection. For a non-transactional database, 260null must be specified. 261@param key the secondary key 262used as input. It must be initialized with a non-null byte array by the 263caller. 264@param pKey the primary key 265used as input. It must be initialized with a non-null byte array by the 266caller. 267@param data the primary data 268returned as output. Its byte array does not need to be initialized by the 269caller. 270<p> 271@param lockMode the locking attributes; if null, default attributes are used. 272<p> 273@return {@link com.sleepycat.db.OperationStatus#NOTFOUND OperationStatus.NOTFOUND} if no matching key/data pair is 274found; {@link com.sleepycat.db.OperationStatus#KEYEMPTY OperationStatus.KEYEMPTY} if the database is a Queue or Recno database and the specified key exists, but was never explicitly created by the application or was later deleted; otherwise, {@link com.sleepycat.db.OperationStatus#SUCCESS OperationStatus.SUCCESS}. 275<p> 276<p> 277@throws DeadlockException if the operation was selected to resolve a 278deadlock. 279<p> 280@throws IllegalArgumentException if an invalid parameter was specified. 281<p> 282@throws DatabaseException if a failure occurs. 283 */ 284 public OperationStatus getSearchBoth(final Transaction txn, 285 final DatabaseEntry key, 286 final DatabaseEntry pKey, 287 final DatabaseEntry data, 288 final LockMode lockMode) 289 throws DatabaseException { 290 291 return OperationStatus.fromInt( 292 db.pget((txn == null) ? null : txn.txn, key, pKey, data, 293 DbConstants.DB_GET_BOTH | LockMode.getFlag(lockMode) | 294 ((data == null) ? 0 : data.getMultiFlag()))); 295 } 296 297 /** 298 Retrieves the key/data pair associated with the specific numbered record of the database. 299<p> 300The data field of the specified key must be a byte array containing a 301record number, as described in {@link com.sleepycat.db.DatabaseEntry DatabaseEntry}. This determines 302the record to be retrieved. 303<p> 304For this method to be called, the underlying database must be of type 305Btree, and it must have been configured to support record numbers. 306<p> 307If this method fails for any reason, the position of the cursor will be 308unchanged. 309@throws NullPointerException if a DatabaseEntry parameter is null or 310does not contain a required non-null byte array. 311<p> 312@throws DeadlockException if the operation was selected to resolve a 313deadlock. 314<p> 315@throws IllegalArgumentException if an invalid parameter was specified. 316<p> 317@throws DatabaseException if a failure occurs. 318<p> 319@param key the secondary key 320returned as output. Its byte array does not need to be initialized by the 321caller. 322@param pKey the primary key 323returned as output. Its byte array does not need to be initialized by the 324caller. 325@param data the primary data 326returned as output. Multiple results can be retrieved by passing an object 327that is a subclass of {@link com.sleepycat.db.MultipleEntry MultipleEntry}, otherwise its byte array does not 328need to be initialized by the caller. 329@param lockMode the locking attributes; if null, default attributes are used. 330@return {@link com.sleepycat.db.OperationStatus#NOTFOUND OperationStatus.NOTFOUND} if no matching key/data pair is 331found; {@link com.sleepycat.db.OperationStatus#KEYEMPTY OperationStatus.KEYEMPTY} if the database is a Queue or Recno database and the specified key exists, but was never explicitly created by the application or was later deleted; otherwise, {@link com.sleepycat.db.OperationStatus#SUCCESS OperationStatus.SUCCESS}. 332 */ 333 public OperationStatus getSearchRecordNumber(final Transaction txn, 334 final DatabaseEntry key, 335 final DatabaseEntry pKey, 336 final DatabaseEntry data, 337 final LockMode lockMode) 338 throws DatabaseException { 339 340 return OperationStatus.fromInt( 341 db.pget((txn == null) ? null : txn.txn, key, pKey, data, 342 DbConstants.DB_SET_RECNO | LockMode.getFlag(lockMode) | 343 ((data == null) ? 0 : data.getMultiFlag()))); 344 } 345} 346