1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002,2008 Oracle. All rights reserved. 5 * 6 * $Id: SecondaryConfig.java,v 12.12 2008/03/10 13:22:16 mjc Exp $ 7 */ 8 9package com.sleepycat.db; 10 11import com.sleepycat.db.internal.Db; 12import com.sleepycat.db.internal.DbConstants; 13import com.sleepycat.db.internal.DbEnv; 14import com.sleepycat.db.internal.DbTxn; 15 16/** 17The configuration properties of a <code>SecondaryDatabase</code> extend 18those of a primary <code>Database</code>. 19The secondary database configuration is specified when calling {@link 20Environment#openSecondaryDatabase Environment.openSecondaryDatabase}. 21<p> 22To create a configuration object with default attributes: 23<pre> 24 SecondaryConfig config = new SecondaryConfig(); 25</pre> 26To set custom attributes: 27<pre> 28 SecondaryConfig config = new SecondaryConfig(); 29 config.setAllowCreate(true); 30 config.setSortedDuplicates(true); 31 config.setKeyCreator(new MyKeyCreator()); 32</pre> 33<p> 34<hr> 35<p> 36NOTE: There are two situations where the use of secondary databases without 37transactions requires special consideration. When using a transactional 38database or when doing read operations only, this note does not apply. 39<ul> 40<li>If secondary is configured to not allow duplicates, when the secondary 41is being updated it is possible that an error will occur when the secondary 42key value in a record being added is already present in the database. A 43<code>DatabaseException</code> will be thrown in this situation.</li> 44<li>If a foreign key constraint is configured with the delete action 45<code>ABORT</code> (the default setting), a <code>DatabaseException</code> 46will be thrown if an attempt is made to delete a referenced foreign 47key.</li> 48</ul> 49In both cases, the operation will be partially complete because the primary 50database record will have already been updated or deleted. In the presence 51of transactions, the exception will cause the transaction to abort. Without 52transactions, it is the responsibility of the caller to handle the results 53of the incomplete update or to take steps to prevent this situation from 54happening in the first place. 55<p> 56</hr> 57<p> 58@see Environment#openSecondaryDatabase Environment.openSecondaryDatabase 59@see SecondaryDatabase 60*/ 61public class SecondaryConfig extends DatabaseConfig implements Cloneable { 62 /* 63 * For internal use, to allow null as a valid value for 64 * the config parameter. 65 */ 66 public static final SecondaryConfig DEFAULT = new SecondaryConfig(); 67 68 /* package */ 69 static SecondaryConfig checkNull(SecondaryConfig config) { 70 return (config == null) ? DEFAULT : config; 71 } 72 73 private boolean allowPopulate; 74 private boolean immutableSecondaryKey; 75 private Db foreign; 76 private ForeignKeyDeleteAction fkDelAction; 77 private ForeignKeyNullifier keyNullifier; 78 private ForeignMultiKeyNullifier multiKeyNullifier; 79 private SecondaryKeyCreator keyCreator; 80 private SecondaryMultiKeyCreator multiKeyCreator; 81 82 /** 83 Creates an instance with the system's default settings. 84 */ 85 public SecondaryConfig() { 86 } 87 88 /** 89 Specifies whether automatic population of the secondary is allowed. 90 <p> 91 If automatic population is allowed, when the secondary database is 92 opened it is checked to see if it is empty. If it is empty, the 93 primary database is read in its entirety and keys are added to the 94 secondary database using the information read from the primary. 95 <p> 96 If this property is set to true and the database is transactional, the 97 population of the secondary will be done within the explicit or auto-commit 98 transaction that is used to open the database. 99 <p> 100 @param allowPopulate whether automatic population of the secondary is 101 allowed. 102 */ 103 public void setAllowPopulate(final boolean allowPopulate) { 104 this.allowPopulate = allowPopulate; 105 } 106 107 /** 108 Returns whether automatic population of the secondary is allowed. If 109 {@link #setAllowPopulate} has not been called, this method returns 110 false. 111 <p> 112 @return whether automatic population of the secondary is allowed. 113 <p> 114 @see #setAllowPopulate 115 */ 116 public boolean getAllowPopulate() { 117 return allowPopulate; 118 } 119 120 /** 121 Specifies whether the secondary key is immutable. 122 <p> 123 Specifying that a secondary key is immutable can be used to optimize 124 updates when the secondary key in a primary record will never be changed 125 after that primary record is inserted. For immutable secondary keys, a 126 best effort is made to avoid calling 127 <code>SecondaryKeyCreator.createSecondaryKey</code> when a primary record 128 is updated. This optimization may reduce the overhead of an update 129 operation significantly if the <code>createSecondaryKey</code> operation is 130 expensive. 131 <p> 132 Be sure to set this property to true only if the secondary key in the 133 primary record is never changed. If this rule is violated, the secondary 134 index will become corrupted, that is, it will become out of sync with the 135 primary. 136 <p> 137 @param immutableSecondaryKey whether the secondary key is immutable. 138 */ 139 public void setImmutableSecondaryKey(final boolean immutableSecondaryKey) { 140 this.immutableSecondaryKey = immutableSecondaryKey; 141 } 142 143 /** 144 Returns whether the secondary key is immutable. If 145 {@link #setImmutableSecondaryKey} has not been called, this method returns 146 false. 147 <p> 148 @return whether the secondary key is immutable. 149 <p> 150 @see #setImmutableSecondaryKey 151 */ 152 public boolean getImmutableSecondaryKey() { 153 return immutableSecondaryKey; 154 } 155 156 /** 157 Specifies the user-supplied object used for creating single-valued 158 secondary keys. 159 <p> 160 Unless the primary database is read-only, a key creator is required 161 when opening a secondary database. Either a KeyCreator or MultiKeyCreator 162 must be specified, but both may not be specified. 163 <p> 164 Unless the primary database is read-only, a key creator is required 165 when opening a secondary database. 166 <p> 167 @param keyCreator the user-supplied object used for creating single-valued 168 secondary keys. 169 */ 170 public void setKeyCreator(final SecondaryKeyCreator keyCreator) { 171 this.keyCreator = keyCreator; 172 } 173 174 /** 175 Returns the user-supplied object used for creating single-valued secondary 176 keys. 177 <p> 178 @return the user-supplied object used for creating single-valued secondary 179 keys. 180 <p> 181 @see #setKeyCreator 182 */ 183 public SecondaryKeyCreator getKeyCreator() { 184 return keyCreator; 185 } 186 187 /** 188 Specifies the user-supplied object used for creating multi-valued 189 secondary keys. 190 <p> 191 Unless the primary database is read-only, a key creator is required 192 when opening a secondary database. Either a KeyCreator or MultiKeyCreator 193 must be specified, but both may not be specified. 194 <p> 195 @param multiKeyCreator the user-supplied object used for creating multi-valued 196 secondary keys. 197 */ 198 public void setMultiKeyCreator(final SecondaryMultiKeyCreator multiKeyCreator) { 199 this.multiKeyCreator = multiKeyCreator; 200 } 201 202 /** 203 Returns the user-supplied object used for creating multi-valued secondary 204 keys. 205 <p> 206 @return the user-supplied object used for creating multi-valued secondary 207 keys. 208 <p> 209 @see #setKeyCreator 210 */ 211 public SecondaryMultiKeyCreator getMultiKeyCreator() { 212 return multiKeyCreator; 213 } 214 215 public void setForeignKeyDatabase(Database foreignDb){ 216 this.foreign = foreignDb.db; 217 } 218 219 public Db getForeignKeyDatabase(){ 220 return foreign; 221 } 222 223 public void setForeignKeyDeleteAction(ForeignKeyDeleteAction action){ 224 this.fkDelAction = action; 225 } 226 227 public ForeignKeyDeleteAction getForeignKeyDeleteAction(){ 228 return fkDelAction; 229 } 230 231 public void setForeignKeyNullifier(ForeignKeyNullifier keyNullifier){ 232 this.keyNullifier = keyNullifier; 233 } 234 235 public ForeignKeyNullifier getForeignKeyNullifier(){ 236 return keyNullifier; 237 } 238 239 public void setForeignMultiKeyNullifier(ForeignMultiKeyNullifier multiKeyNullifier){ 240 this.multiKeyNullifier = multiKeyNullifier; 241 } 242 243 public ForeignMultiKeyNullifier getForeignMultiKeyNullifier(){ 244 return multiKeyNullifier; 245 } 246 247 /* package */ 248 Db openSecondaryDatabase(final DbEnv dbenv, 249 final DbTxn txn, 250 final String fileName, 251 final String databaseName, 252 final Db primary) 253 throws DatabaseException, java.io.FileNotFoundException { 254 int associateFlags = 0; 255 int foreignFlags = 0; 256 associateFlags |= allowPopulate ? DbConstants.DB_CREATE : 0; 257 if (getTransactional() && txn == null) 258 associateFlags |= DbConstants.DB_AUTO_COMMIT; 259 if (immutableSecondaryKey) 260 associateFlags |= DbConstants.DB_IMMUTABLE_KEY; 261 262 final Db db = super.openDatabase(dbenv, txn, fileName, databaseName); 263 boolean succeeded = false; 264 try { 265 /* 266 * The multi-key creator must be set before the call to associate 267 * so that we can work out whether the C API callback should be 268 * set or not. 269 */ 270 db.set_secmultikey_create(multiKeyCreator); 271 primary.associate(txn, db, keyCreator, associateFlags); 272 if (foreign != null){ 273 db.set_foreignmultikey_nullifier(multiKeyNullifier); 274 foreign.associate_foreign(db, keyNullifier, foreignFlags | fkDelAction.getId()); 275 } 276 succeeded = true; 277 return db; 278 } finally { 279 if (!succeeded) 280 try { 281 db.close(0); 282 } catch (Throwable t) { 283 // Ignore it -- there is already an exception in flight. 284 } 285 } 286 } 287 288 /* package */ 289 SecondaryConfig(final Db db) 290 throws DatabaseException { 291 292 super(db); 293 294 // XXX: There is no way to find out what flags were passed to associate. 295 allowPopulate = false; 296 immutableSecondaryKey = false; 297 keyCreator = db.get_seckey_create(); 298 multiKeyCreator = db.get_secmultikey_create(); 299 } 300} 301 302