001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: SubIndex.java,v 1.12.2.3 2008/01/07 15:14:18 cwl Exp $
007: */
008:
009: package com.sleepycat.persist;
010:
011: import java.util.Map;
012: import java.util.SortedMap;
013:
014: import com.sleepycat.bind.EntityBinding;
015: import com.sleepycat.bind.EntryBinding;
016: import com.sleepycat.collections.StoredSortedMap;
017: import com.sleepycat.je.Cursor;
018: import com.sleepycat.je.CursorConfig;
019: import com.sleepycat.je.DatabaseEntry;
020: import com.sleepycat.je.DatabaseException;
021: import com.sleepycat.je.Environment;
022: import com.sleepycat.je.LockMode;
023: import com.sleepycat.je.OperationStatus;
024: import com.sleepycat.je.SecondaryCursor;
025: import com.sleepycat.je.SecondaryDatabase;
026: import com.sleepycat.je.Transaction;
027: import com.sleepycat.util.keyrange.KeyRange;
028: import com.sleepycat.util.keyrange.RangeCursor;
029:
030: /**
031: * The EntityIndex returned by SecondaryIndex.subIndex. A SubIndex, in JE
032: * internal terms, is a duplicates btree for a single key in the main btree.
033: * From the user's viewpoint, the keys are primary keys. This class implements
034: * that viewpoint. In general, getSearchBoth and getSearchBothRange are used
035: * where in a normal index getSearchKey and getSearchRange would be used. The
036: * main tree key is always implied, not passed as a parameter.
037: *
038: * @author Mark Hayes
039: */
040: class SubIndex<PK, E> implements EntityIndex<PK, E> {
041:
042: private SecondaryIndex<?, PK, E> secIndex;
043: private SecondaryDatabase db;
044: private boolean transactional;
045: private DatabaseEntry keyEntry;
046: private Object keyObject;
047: private KeyRange singleKeyRange;
048: private EntryBinding pkeyBinding;
049: private KeyRange emptyPKeyRange;
050: private EntityBinding entityBinding;
051: private ValueAdapter<PK> keyAdapter;
052: private ValueAdapter<E> entityAdapter;
053: private SortedMap<PK, E> map;
054:
055: <SK> SubIndex(SecondaryIndex<SK, PK, E> secIndex,
056: EntityBinding entityBinding, SK key) {
057:
058: this .secIndex = secIndex;
059: db = secIndex.getDatabase();
060: transactional = secIndex.transactional;
061:
062: keyObject = key;
063: keyEntry = new DatabaseEntry();
064: secIndex.keyBinding.objectToEntry(key, keyEntry);
065: singleKeyRange = secIndex.emptyRange.subRange(keyEntry);
066:
067: PrimaryIndex<PK, E> priIndex = secIndex.getPrimaryIndex();
068: pkeyBinding = priIndex.keyBinding;
069: emptyPKeyRange = priIndex.emptyRange;
070: this .entityBinding = entityBinding;
071:
072: keyAdapter = new PrimaryKeyValueAdapter<PK>(priIndex.keyClass,
073: priIndex.keyBinding);
074: entityAdapter = secIndex.entityAdapter;
075: }
076:
077: public boolean contains(PK key) throws DatabaseException {
078:
079: return contains(null, key, null);
080: }
081:
082: public boolean contains(Transaction txn, PK key, LockMode lockMode)
083: throws DatabaseException {
084:
085: DatabaseEntry pkeyEntry = new DatabaseEntry();
086: DatabaseEntry dataEntry = BasicIndex.NO_RETURN_ENTRY;
087: pkeyBinding.objectToEntry(key, pkeyEntry);
088:
089: OperationStatus status = db.getSearchBoth(txn, keyEntry,
090: pkeyEntry, dataEntry, lockMode);
091: return (status == OperationStatus.SUCCESS);
092: }
093:
094: public E get(PK key) throws DatabaseException {
095:
096: return get(null, key, null);
097: }
098:
099: public E get(Transaction txn, PK key, LockMode lockMode)
100: throws DatabaseException {
101:
102: DatabaseEntry pkeyEntry = new DatabaseEntry();
103: DatabaseEntry dataEntry = new DatabaseEntry();
104: pkeyBinding.objectToEntry(key, pkeyEntry);
105:
106: OperationStatus status = db.getSearchBoth(txn, keyEntry,
107: pkeyEntry, dataEntry, lockMode);
108:
109: if (status == OperationStatus.SUCCESS) {
110: return (E) entityBinding
111: .entryToObject(pkeyEntry, dataEntry);
112: } else {
113: return null;
114: }
115: }
116:
117: public long count() throws DatabaseException {
118:
119: EntityCursor<PK> cursor = keys(null,
120: CursorConfig.READ_UNCOMMITTED);
121: try {
122: if (cursor.next() != null) {
123: return cursor.count();
124: } else {
125: return 0;
126: }
127: } finally {
128: cursor.close();
129: }
130: }
131:
132: public boolean delete(PK key) throws DatabaseException {
133:
134: return delete(null, key);
135: }
136:
137: public boolean delete(Transaction txn, PK key)
138: throws DatabaseException {
139:
140: DatabaseEntry pkeyEntry = new DatabaseEntry();
141: DatabaseEntry dataEntry = BasicIndex.NO_RETURN_ENTRY;
142: pkeyBinding.objectToEntry(key, pkeyEntry);
143:
144: boolean autoCommit = false;
145: Environment env = db.getEnvironment();
146: if (transactional && txn == null
147: && env.getThreadTransaction() == null) {
148: txn = env.beginTransaction(null, null);
149: autoCommit = true;
150: }
151:
152: boolean failed = true;
153: OperationStatus status;
154: SecondaryCursor cursor = db.openSecondaryCursor(txn, null);
155: try {
156: status = cursor.getSearchBoth(keyEntry, pkeyEntry,
157: dataEntry, LockMode.RMW);
158: if (status == OperationStatus.SUCCESS) {
159: status = cursor.delete();
160: }
161: failed = false;
162: } finally {
163: cursor.close();
164: if (autoCommit) {
165: if (failed) {
166: txn.abort();
167: } else {
168: txn.commit();
169: }
170: }
171: }
172:
173: return (status == OperationStatus.SUCCESS);
174: }
175:
176: public EntityCursor<PK> keys() throws DatabaseException {
177:
178: return keys(null, null);
179: }
180:
181: public EntityCursor<PK> keys(Transaction txn, CursorConfig config)
182: throws DatabaseException {
183:
184: return cursor(txn, null, keyAdapter, config);
185: }
186:
187: public EntityCursor<E> entities() throws DatabaseException {
188:
189: return cursor(null, null, entityAdapter, null);
190: }
191:
192: public EntityCursor<E> entities(Transaction txn, CursorConfig config)
193: throws DatabaseException {
194:
195: return cursor(txn, null, entityAdapter, config);
196: }
197:
198: public EntityCursor<PK> keys(PK fromKey, boolean fromInclusive,
199: PK toKey, boolean toInclusive) throws DatabaseException {
200:
201: return cursor(null, fromKey, fromInclusive, toKey, toInclusive,
202: keyAdapter, null);
203: }
204:
205: public EntityCursor<PK> keys(Transaction txn, PK fromKey,
206: boolean fromInclusive, PK toKey, boolean toInclusive,
207: CursorConfig config) throws DatabaseException {
208:
209: return cursor(txn, fromKey, fromInclusive, toKey, toInclusive,
210: keyAdapter, config);
211: }
212:
213: public EntityCursor<E> entities(PK fromKey, boolean fromInclusive,
214: PK toKey, boolean toInclusive) throws DatabaseException {
215:
216: return cursor(null, fromKey, fromInclusive, toKey, toInclusive,
217: entityAdapter, null);
218: }
219:
220: public EntityCursor<E> entities(Transaction txn, PK fromKey,
221: boolean fromInclusive, PK toKey, boolean toInclusive,
222: CursorConfig config) throws DatabaseException {
223:
224: return cursor(txn, fromKey, fromInclusive, toKey, toInclusive,
225: entityAdapter, config);
226: }
227:
228: /*
229: public ForwardCursor<PK> unsortedKeys(KeySelector<PK> selector)
230: throws DatabaseException {
231:
232: return unsortedKeys(null, selector, null);
233: }
234:
235: public ForwardCursor<PK> unsortedKeys(Transaction txn,
236: KeySelector<PK> selector,
237: CursorConfig config)
238: throws DatabaseException {
239:
240: throw new UnsupportedOperationException();
241: }
242:
243: public ForwardCursor<E> unsortedEntities(KeySelector<PK> selector)
244: throws DatabaseException {
245:
246: return unsortedEntities(null, selector, null);
247: }
248:
249: public ForwardCursor<E> unsortedEntities(Transaction txn,
250: KeySelector<PK> selector,
251: CursorConfig config)
252: throws DatabaseException {
253:
254: throw new UnsupportedOperationException();
255: }
256: */
257:
258: private <V> EntityCursor<V> cursor(Transaction txn, PK fromKey,
259: boolean fromInclusive, PK toKey, boolean toInclusive,
260: ValueAdapter<V> adapter, CursorConfig config)
261: throws DatabaseException {
262:
263: DatabaseEntry fromEntry = null;
264: if (fromKey != null) {
265: fromEntry = new DatabaseEntry();
266: pkeyBinding.objectToEntry(fromKey, fromEntry);
267: }
268: DatabaseEntry toEntry = null;
269: if (toKey != null) {
270: toEntry = new DatabaseEntry();
271: pkeyBinding.objectToEntry(toKey, toEntry);
272: }
273: KeyRange pkeyRange = emptyPKeyRange.subRange(fromEntry,
274: fromInclusive, toEntry, toInclusive);
275: return cursor(txn, pkeyRange, adapter, config);
276: }
277:
278: private <V> EntityCursor<V> cursor(Transaction txn,
279: KeyRange pkeyRange, ValueAdapter<V> adapter,
280: CursorConfig config) throws DatabaseException {
281:
282: Cursor cursor = db.openCursor(txn, config);
283: RangeCursor rangeCursor = new RangeCursor(singleKeyRange,
284: pkeyRange, cursor);
285: return new SubIndexCursor<V>(rangeCursor, adapter);
286: }
287:
288: public Map<PK, E> map() {
289: return sortedMap();
290: }
291:
292: public synchronized SortedMap<PK, E> sortedMap() {
293: if (map == null) {
294: map = (SortedMap) ((StoredSortedMap) secIndex.sortedMap())
295: .duplicatesMap(keyObject, pkeyBinding);
296: }
297: return map;
298: }
299: }
|