1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002,2008 Oracle. All rights reserved. 5 * 6 * $Id: SubIndex.java,v 1.1 2008/02/07 17:12:26 mark Exp $ 7 */ 8 9package com.sleepycat.persist; 10 11import java.util.Map; 12import java.util.SortedMap; 13 14import com.sleepycat.bind.EntityBinding; 15import com.sleepycat.bind.EntryBinding; 16import com.sleepycat.collections.StoredSortedMap; 17import com.sleepycat.compat.DbCompat; 18import com.sleepycat.db.Cursor; 19import com.sleepycat.db.CursorConfig; 20import com.sleepycat.db.DatabaseEntry; 21import com.sleepycat.db.DatabaseException; 22import com.sleepycat.db.Environment; 23import com.sleepycat.db.LockMode; 24import com.sleepycat.db.OperationStatus; 25import com.sleepycat.db.SecondaryCursor; 26import com.sleepycat.db.SecondaryDatabase; 27import com.sleepycat.db.Transaction; 28import com.sleepycat.util.keyrange.KeyRange; 29import com.sleepycat.util.keyrange.RangeCursor; 30 31/** 32 * The EntityIndex returned by SecondaryIndex.subIndex. A SubIndex, in JE 33 * internal terms, is a duplicates btree for a single key in the main btree. 34 * From the user's viewpoint, the keys are primary keys. This class implements 35 * that viewpoint. In general, getSearchBoth and getSearchBothRange are used 36 * where in a normal index getSearchKey and getSearchRange would be used. The 37 * main tree key is always implied, not passed as a parameter. 38 * 39 * @author Mark Hayes 40 */ 41class SubIndex<PK,E> implements EntityIndex<PK,E> { 42 43 private SecondaryIndex<?,PK,E> secIndex; 44 private SecondaryDatabase db; 45 private boolean transactional; 46 private boolean sortedDups; 47 private boolean locking; 48 private DatabaseEntry keyEntry; 49 private Object keyObject; 50 private KeyRange singleKeyRange; 51 private EntryBinding pkeyBinding; 52 private KeyRange emptyPKeyRange; 53 private EntityBinding entityBinding; 54 private ValueAdapter<PK> keyAdapter; 55 private ValueAdapter<E> entityAdapter; 56 private SortedMap<PK,E> map; 57 58 <SK> SubIndex(SecondaryIndex<SK,PK,E> secIndex, 59 EntityBinding entityBinding, 60 SK key) 61 throws DatabaseException { 62 63 this.secIndex = secIndex; 64 db = secIndex.getDatabase(); 65 transactional = secIndex.transactional; 66 sortedDups = secIndex.sortedDups; 67 locking = 68 DbCompat.getInitializeLocking(db.getEnvironment().getConfig()); 69 70 keyObject = key; 71 keyEntry = new DatabaseEntry(); 72 secIndex.keyBinding.objectToEntry(key, keyEntry); 73 singleKeyRange = secIndex.emptyRange.subRange(keyEntry); 74 75 PrimaryIndex<PK,E> priIndex = secIndex.getPrimaryIndex(); 76 pkeyBinding = priIndex.keyBinding; 77 emptyPKeyRange = priIndex.emptyRange; 78 this.entityBinding = entityBinding; 79 80 keyAdapter = new PrimaryKeyValueAdapter<PK> 81 (priIndex.keyClass, priIndex.keyBinding); 82 entityAdapter = secIndex.entityAdapter; 83 } 84 85 public boolean contains(PK key) 86 throws DatabaseException { 87 88 return contains(null, key, null); 89 } 90 91 public boolean contains(Transaction txn, PK key, LockMode lockMode) 92 throws DatabaseException { 93 94 DatabaseEntry pkeyEntry = new DatabaseEntry(); 95 DatabaseEntry dataEntry = BasicIndex.NO_RETURN_ENTRY; 96 pkeyBinding.objectToEntry(key, pkeyEntry); 97 98 OperationStatus status = 99 db.getSearchBoth(txn, keyEntry, pkeyEntry, dataEntry, lockMode); 100 return (status == OperationStatus.SUCCESS); 101 } 102 103 public E get(PK key) 104 throws DatabaseException { 105 106 return get(null, key, null); 107 } 108 109 public E get(Transaction txn, PK key, LockMode lockMode) 110 throws DatabaseException { 111 112 DatabaseEntry pkeyEntry = new DatabaseEntry(); 113 DatabaseEntry dataEntry = new DatabaseEntry(); 114 pkeyBinding.objectToEntry(key, pkeyEntry); 115 116 OperationStatus status = 117 db.getSearchBoth(txn, keyEntry, pkeyEntry, dataEntry, lockMode); 118 119 if (status == OperationStatus.SUCCESS) { 120 return (E) entityBinding.entryToObject(pkeyEntry, dataEntry); 121 } else { 122 return null; 123 } 124 } 125 126 public long count() 127 throws DatabaseException { 128 129 CursorConfig cursorConfig = locking ? 130 CursorConfig.READ_UNCOMMITTED : null; 131 EntityCursor<PK> cursor = keys(null, cursorConfig); 132 try { 133 if (cursor.next() != null) { 134 return cursor.count(); 135 } else { 136 return 0; 137 } 138 } finally { 139 cursor.close(); 140 } 141 } 142 143 public boolean delete(PK key) 144 throws DatabaseException { 145 146 return delete(null, key); 147 } 148 149 public boolean delete(Transaction txn, PK key) 150 throws DatabaseException { 151 152 DatabaseEntry pkeyEntry = new DatabaseEntry(); 153 DatabaseEntry dataEntry = BasicIndex.NO_RETURN_ENTRY; 154 pkeyBinding.objectToEntry(key, pkeyEntry); 155 156 boolean autoCommit = false; 157 Environment env = db.getEnvironment(); 158 if (transactional && 159 txn == null && 160 DbCompat.getThreadTransaction(env) == null) { 161 txn = env.beginTransaction(null, null); 162 autoCommit = true; 163 } 164 165 boolean failed = true; 166 OperationStatus status; 167 SecondaryCursor cursor = db.openSecondaryCursor(txn, null); 168 try { 169 status = cursor.getSearchBoth 170 (keyEntry, pkeyEntry, dataEntry, 171 locking ? LockMode.RMW : null); 172 if (status == OperationStatus.SUCCESS) { 173 status = cursor.delete(); 174 } 175 failed = false; 176 } finally { 177 cursor.close(); 178 if (autoCommit) { 179 if (failed) { 180 txn.abort(); 181 } else { 182 txn.commit(); 183 } 184 } 185 } 186 187 return (status == OperationStatus.SUCCESS); 188 } 189 190 public EntityCursor<PK> keys() 191 throws DatabaseException { 192 193 return keys(null, null); 194 } 195 196 public EntityCursor<PK> keys(Transaction txn, CursorConfig config) 197 throws DatabaseException { 198 199 return cursor(txn, null, keyAdapter, config); 200 } 201 202 public EntityCursor<E> entities() 203 throws DatabaseException { 204 205 return cursor(null, null, entityAdapter, null); 206 } 207 208 public EntityCursor<E> entities(Transaction txn, 209 CursorConfig config) 210 throws DatabaseException { 211 212 return cursor(txn, null, entityAdapter, config); 213 } 214 215 public EntityCursor<PK> keys(PK fromKey, 216 boolean fromInclusive, 217 PK toKey, 218 boolean toInclusive) 219 throws DatabaseException { 220 221 return cursor(null, fromKey, fromInclusive, toKey, toInclusive, 222 keyAdapter, null); 223 } 224 225 public EntityCursor<PK> keys(Transaction txn, 226 PK fromKey, 227 boolean fromInclusive, 228 PK toKey, 229 boolean toInclusive, 230 CursorConfig config) 231 throws DatabaseException { 232 233 return cursor(txn, fromKey, fromInclusive, toKey, toInclusive, 234 keyAdapter, config); 235 } 236 237 public EntityCursor<E> entities(PK fromKey, 238 boolean fromInclusive, 239 PK toKey, 240 boolean toInclusive) 241 throws DatabaseException { 242 243 return cursor(null, fromKey, fromInclusive, toKey, toInclusive, 244 entityAdapter, null); 245 } 246 247 public EntityCursor<E> entities(Transaction txn, 248 PK fromKey, 249 boolean fromInclusive, 250 PK toKey, 251 boolean toInclusive, 252 CursorConfig config) 253 throws DatabaseException { 254 255 return cursor(txn, fromKey, fromInclusive, toKey, toInclusive, 256 entityAdapter, config); 257 } 258 259 /* 260 public ForwardCursor<PK> unsortedKeys(KeySelector<PK> selector) 261 throws DatabaseException { 262 263 return unsortedKeys(null, selector, null); 264 } 265 266 public ForwardCursor<PK> unsortedKeys(Transaction txn, 267 KeySelector<PK> selector, 268 CursorConfig config) 269 throws DatabaseException { 270 271 throw new UnsupportedOperationException(); 272 } 273 274 public ForwardCursor<E> unsortedEntities(KeySelector<PK> selector) 275 throws DatabaseException { 276 277 return unsortedEntities(null, selector, null); 278 } 279 280 public ForwardCursor<E> unsortedEntities(Transaction txn, 281 KeySelector<PK> selector, 282 CursorConfig config) 283 throws DatabaseException { 284 285 throw new UnsupportedOperationException(); 286 } 287 */ 288 289 private <V> EntityCursor<V> cursor(Transaction txn, 290 PK fromKey, 291 boolean fromInclusive, 292 PK toKey, 293 boolean toInclusive, 294 ValueAdapter<V> adapter, 295 CursorConfig config) 296 throws DatabaseException { 297 298 DatabaseEntry fromEntry = null; 299 if (fromKey != null) { 300 fromEntry = new DatabaseEntry(); 301 pkeyBinding.objectToEntry(fromKey, fromEntry); 302 } 303 DatabaseEntry toEntry = null; 304 if (toKey != null) { 305 toEntry = new DatabaseEntry(); 306 pkeyBinding.objectToEntry(toKey, toEntry); 307 } 308 KeyRange pkeyRange = emptyPKeyRange.subRange 309 (fromEntry, fromInclusive, toEntry, toInclusive); 310 return cursor(txn, pkeyRange, adapter, config); 311 } 312 313 private <V> EntityCursor<V> cursor(Transaction txn, 314 KeyRange pkeyRange, 315 ValueAdapter<V> adapter, 316 CursorConfig config) 317 throws DatabaseException { 318 319 Cursor cursor = db.openCursor(txn, config); 320 RangeCursor rangeCursor = 321 new RangeCursor(singleKeyRange, pkeyRange, sortedDups, cursor); 322 return new SubIndexCursor<V>(rangeCursor, adapter); 323 } 324 325 public Map<PK,E> map() { 326 return sortedMap(); 327 } 328 329 public synchronized SortedMap<PK,E> sortedMap() { 330 if (map == null) { 331 map = (SortedMap) ((StoredSortedMap) secIndex.sortedMap()). 332 duplicatesMap(keyObject, pkeyBinding); 333 } 334 return map; 335 } 336} 337