/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2002, 2015 Oracle and/or its affiliates. All rights reserved. * */ package com.sleepycat.persist; import java.util.Map; import java.util.SortedMap; import com.sleepycat.bind.EntityBinding; import com.sleepycat.bind.EntryBinding; import com.sleepycat.collections.StoredSortedMap; import com.sleepycat.compat.DbCompat; import com.sleepycat.db.Cursor; import com.sleepycat.db.CursorConfig; import com.sleepycat.db.Database; import com.sleepycat.db.DatabaseEntry; import com.sleepycat.db.DatabaseException; import com.sleepycat.db.Environment; import com.sleepycat.db.LockMode; import com.sleepycat.db.OperationStatus; import com.sleepycat.db.SecondaryCursor; import com.sleepycat.db.SecondaryDatabase; import com.sleepycat.db.Transaction; import com.sleepycat.util.keyrange.KeyRange; import com.sleepycat.util.keyrange.RangeCursor; /** * The EntityIndex returned by SecondaryIndex.subIndex. A SubIndex, in JE * internal terms, is a duplicates btree for a single key in the main btree. * From the user's viewpoint, the keys are primary keys. This class implements * that viewpoint. In general, getSearchBoth and getSearchBothRange are used * where in a normal index getSearchKey and getSearchRange would be used. The * main tree key is always implied, not passed as a parameter. * * @author Mark Hayes */ class SubIndex<PK, E> implements EntityIndex<PK, E> { private SecondaryIndex<?,PK,E> secIndex; private SecondaryDatabase db; private boolean transactional; private boolean sortedDups; private boolean locking; private boolean concurrentDB; private DatabaseEntry keyEntry; private Object keyObject; private KeyRange singleKeyRange; private EntryBinding pkeyBinding; private KeyRange emptyPKeyRange; private EntityBinding entityBinding; private ValueAdapter<PK> keyAdapter; private ValueAdapter<E> entityAdapter; private SortedMap<PK, E> map; <SK> SubIndex(SecondaryIndex<SK, PK, E> secIndex, EntityBinding entityBinding, SK key) throws DatabaseException { this.secIndex = secIndex; db = secIndex.getDatabase(); transactional = secIndex.transactional; sortedDups = secIndex.sortedDups; locking = DbCompat.getInitializeLocking(db.getEnvironment().getConfig()); Environment env = db.getEnvironment(); concurrentDB = DbCompat.getInitializeCDB(env.getConfig()); keyObject = key; keyEntry = new DatabaseEntry(); secIndex.keyBinding.objectToEntry(key, keyEntry); singleKeyRange = secIndex.emptyRange.subRange(keyEntry); PrimaryIndex<PK, E> priIndex = secIndex.getPrimaryIndex(); pkeyBinding = priIndex.keyBinding; emptyPKeyRange = priIndex.emptyRange; this.entityBinding = entityBinding; keyAdapter = new PrimaryKeyValueAdapter<PK> (priIndex.keyClass, priIndex.keyBinding); entityAdapter = secIndex.entityAdapter; } public Database getDatabase() { return db; } public boolean contains(PK key) throws DatabaseException { return contains(null, key, null); } public boolean contains(Transaction txn, PK key, LockMode lockMode) throws DatabaseException { DatabaseEntry pkeyEntry = new DatabaseEntry(); DatabaseEntry dataEntry = BasicIndex.NO_RETURN_ENTRY; pkeyBinding.objectToEntry(key, pkeyEntry); OperationStatus status = db.getSearchBoth(txn, keyEntry, pkeyEntry, dataEntry, lockMode); return (status == OperationStatus.SUCCESS); } public E get(PK key) throws DatabaseException { return get(null, key, null); } public E get(Transaction txn, PK key, LockMode lockMode) throws DatabaseException { DatabaseEntry pkeyEntry = new DatabaseEntry(); DatabaseEntry dataEntry = new DatabaseEntry(); pkeyBinding.objectToEntry(key, pkeyEntry); OperationStatus status = db.getSearchBoth(txn, keyEntry, pkeyEntry, dataEntry, lockMode); if (status == OperationStatus.SUCCESS) { return (E) entityBinding.entryToObject(pkeyEntry, dataEntry); } else { return null; } } public long count() throws DatabaseException { CursorConfig cursorConfig = locking ? CursorConfig.READ_UNCOMMITTED : null; EntityCursor<PK> cursor = keys(null, cursorConfig); try { if (cursor.next() != null) { return cursor.count(); } else { return 0; } } finally { cursor.close(); } } public boolean delete(PK key) throws DatabaseException { return delete(null, key); } public boolean delete(Transaction txn, PK key) throws DatabaseException { DatabaseEntry pkeyEntry = new DatabaseEntry(); DatabaseEntry dataEntry = BasicIndex.NO_RETURN_ENTRY; pkeyBinding.objectToEntry(key, pkeyEntry); boolean autoCommit = false; Environment env = db.getEnvironment(); if (transactional && txn == null && DbCompat.getThreadTransaction(env) == null) { txn = env.beginTransaction (null, secIndex.getAutoCommitTransactionConfig()); autoCommit = true; } boolean failed = true; OperationStatus status; CursorConfig cursorConfig = null; if (concurrentDB) { cursorConfig = new CursorConfig(); DbCompat.setWriteCursor(cursorConfig, true); } SecondaryCursor cursor = db.openSecondaryCursor(txn, cursorConfig); try { status = cursor.getSearchBoth (keyEntry, pkeyEntry, dataEntry, locking ? LockMode.RMW : null); if (status == OperationStatus.SUCCESS) { status = cursor.delete(); } failed = false; } finally { cursor.close(); if (autoCommit) { if (failed) { txn.abort(); } else { txn.commit(); } } } return (status == OperationStatus.SUCCESS); } public EntityCursor<PK> keys() throws DatabaseException { return keys(null, null); } public EntityCursor<PK> keys(Transaction txn, CursorConfig config) throws DatabaseException { return cursor(txn, null, keyAdapter, config); } public EntityCursor<E> entities() throws DatabaseException { return cursor(null, null, entityAdapter, null); } public EntityCursor<E> entities(Transaction txn, CursorConfig config) throws DatabaseException { return cursor(txn, null, entityAdapter, config); } public EntityCursor<PK> keys(PK fromKey, boolean fromInclusive, PK toKey, boolean toInclusive) throws DatabaseException { return cursor(null, fromKey, fromInclusive, toKey, toInclusive, keyAdapter, null); } public EntityCursor<PK> keys(Transaction txn, PK fromKey, boolean fromInclusive, PK toKey, boolean toInclusive, CursorConfig config) throws DatabaseException { return cursor(txn, fromKey, fromInclusive, toKey, toInclusive, keyAdapter, config); } public EntityCursor<E> entities(PK fromKey, boolean fromInclusive, PK toKey, boolean toInclusive) throws DatabaseException { return cursor(null, fromKey, fromInclusive, toKey, toInclusive, entityAdapter, null); } public EntityCursor<E> entities(Transaction txn, PK fromKey, boolean fromInclusive, PK toKey, boolean toInclusive, CursorConfig config) throws DatabaseException { return cursor(txn, fromKey, fromInclusive, toKey, toInclusive, entityAdapter, config); } private <V> EntityCursor<V> cursor(Transaction txn, PK fromKey, boolean fromInclusive, PK toKey, boolean toInclusive, ValueAdapter<V> adapter, CursorConfig config) throws DatabaseException { DatabaseEntry fromEntry = null; if (fromKey != null) { fromEntry = new DatabaseEntry(); pkeyBinding.objectToEntry(fromKey, fromEntry); } DatabaseEntry toEntry = null; if (toKey != null) { toEntry = new DatabaseEntry(); pkeyBinding.objectToEntry(toKey, toEntry); } KeyRange pkeyRange = emptyPKeyRange.subRange (fromEntry, fromInclusive, toEntry, toInclusive); return cursor(txn, pkeyRange, adapter, config); } private <V> EntityCursor<V> cursor(Transaction txn, KeyRange pkeyRange, ValueAdapter<V> adapter, CursorConfig config) throws DatabaseException { Cursor cursor = db.openCursor(txn, config); RangeCursor rangeCursor = new RangeCursor(singleKeyRange, pkeyRange, sortedDups, cursor); return new SubIndexCursor<V>(rangeCursor, adapter); } public Map<PK, E> map() { return sortedMap(); } public synchronized SortedMap<PK, E> sortedMap() { if (map == null) { map = (SortedMap) ((StoredSortedMap) secIndex.sortedMap()). duplicatesMap(keyObject, pkeyBinding); } return map; } }