KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > je > Cursor


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: Cursor.java,v 1.202 2006/11/28 04:02:53 mark Exp $
7  */

8
9 package com.sleepycat.je;
10
11 import java.util.logging.Level JavaDoc;
12 import java.util.logging.Logger JavaDoc;
13
14 import com.sleepycat.je.dbi.CursorImpl;
15 import com.sleepycat.je.dbi.DatabaseImpl;
16 import com.sleepycat.je.dbi.GetMode;
17 import com.sleepycat.je.dbi.PutMode;
18 import com.sleepycat.je.dbi.RangeRestartException;
19 import com.sleepycat.je.dbi.CursorImpl.KeyChangeStatus;
20 import com.sleepycat.je.dbi.CursorImpl.SearchMode;
21 import com.sleepycat.je.latch.LatchSupport;
22 import com.sleepycat.je.tree.BIN;
23 import com.sleepycat.je.tree.DBIN;
24 import com.sleepycat.je.tree.Key;
25 import com.sleepycat.je.tree.LN;
26 import com.sleepycat.je.tree.Node;
27 import com.sleepycat.je.txn.BuddyLocker;
28 import com.sleepycat.je.txn.LockType;
29 import com.sleepycat.je.txn.Locker;
30 import com.sleepycat.je.txn.LockerFactory;
31 import com.sleepycat.je.utilint.DatabaseUtil;
32 import com.sleepycat.je.utilint.InternalException;
33
34 /**
35  * Javadoc for this public class is generated
36  * via the doc templates in the doc_src directory.
37  */

38 public class Cursor {
39
40     /**
41      * The underlying cursor.
42      */

43     CursorImpl cursorImpl; // Used by subclasses.
44

45     /**
46      * The CursorConfig used to configure this cursor.
47      */

48     CursorConfig config;
49
50     /**
51      * True if update operations are prohibited through this cursor. Update
52      * operations are prohibited if the database is read-only or:
53      *
54      * (1) The database is transactional,
55      *
56      * and
57      *
58      * (2) The user did not supply a txn to the cursor ctor (meaning, the
59      * locker is non-transactional).
60      */

61     private boolean updateOperationsProhibited;
62
63     /**
64      * Handle under which this cursor was created; may be null.
65      */

66     private Database dbHandle;
67
68     /**
69      * Database implementation.
70      */

71     private DatabaseImpl dbImpl;
72
73     /* Attributes */
74     private boolean readUncommittedDefault;
75     private boolean serializableIsolationDefault;
76
77     private Logger JavaDoc logger;
78
79     /**
80      * Creates a cursor for a given user transaction.
81      *
82      * <p>If txn is null, a non-transactional cursor will be created that
83      * releases locks for the prior operation when the next operation
84      * suceeds.</p>
85      */

86     Cursor(Database dbHandle, Transaction txn, CursorConfig cursorConfig)
87         throws DatabaseException {
88
89         if (cursorConfig == null) {
90             cursorConfig = CursorConfig.DEFAULT;
91         }
92
93         Locker locker = LockerFactory.getReadableLocker
94             (dbHandle.getEnvironment(),
95              txn,
96              dbHandle.isTransactional(),
97              false, // retainNonTxnLocks
98
cursorConfig.getReadCommitted());
99
100         init(dbHandle, dbHandle.getDatabaseImpl(), locker,
101              dbHandle.isWritable(), cursorConfig);
102     }
103
104     /**
105      * Creates a cursor for a given locker.
106      *
107      * <p>If locker is null or is non-transactional, a non-transactional cursor
108      * will be created that releases locks for the prior operation when the
109      * next operation suceeds.</p>
110      */

111     Cursor(Database dbHandle, Locker locker, CursorConfig cursorConfig)
112         throws DatabaseException {
113
114         if (cursorConfig == null) {
115             cursorConfig = CursorConfig.DEFAULT;
116         }
117
118         locker = LockerFactory.getReadableLocker
119             (dbHandle.getEnvironment(),
120              dbHandle,
121              locker,
122              false, // retainNonTxnLocks
123
cursorConfig.getReadCommitted());
124
125         init(dbHandle, dbHandle.getDatabaseImpl(), locker,
126              dbHandle.isWritable(), cursorConfig);
127     }
128
129     /**
130      * Creates a cursor for a given locker and no db handle.
131      *
132      * <p>The locker parameter must be non-null. With this constructor, we use
133      * the given locker without applying any special rules for different
134      * isolation levels -- the caller must supply the correct locker.</p>
135      */

136     Cursor(DatabaseImpl dbImpl, Locker locker, CursorConfig cursorConfig)
137         throws DatabaseException {
138
139         if (cursorConfig == null) {
140             cursorConfig = CursorConfig.DEFAULT;
141         }
142
143         init(null, dbImpl, locker, true, cursorConfig);
144     }
145
146     private void init(Database dbHandle,
147               DatabaseImpl dbImpl,
148                       Locker locker,
149               boolean isWritable,
150                       CursorConfig cursorConfig)
151         throws DatabaseException {
152
153         assert locker != null;
154         assert dbImpl != null;
155
156         cursorImpl = new CursorImpl(dbImpl,
157                                     locker,
158                                     false /*retainNonTxnLocks*/);
159
160         /* Perform eviction for user cursors. */
161         cursorImpl.setAllowEviction(true);
162
163         readUncommittedDefault =
164             cursorConfig.getReadUncommitted() ||
165             locker.isReadUncommittedDefault();
166
167         serializableIsolationDefault =
168             cursorImpl.getLocker().isSerializableIsolation();
169
170         updateOperationsProhibited =
171             (dbImpl.isTransactional() && !locker.isTransactional()) ||
172             !isWritable;
173
174         this.dbImpl = dbImpl;
175         this.dbHandle = dbHandle;
176         if (dbHandle != null) {
177             dbHandle.addCursor(this);
178         }
179     this.config = cursorConfig;
180     this.logger = dbImpl.getDbEnvironment().getLogger();
181     }
182
183     /**
184      * Copy constructor.
185      */

186     Cursor(Cursor cursor, boolean samePosition)
187         throws DatabaseException {
188
189         readUncommittedDefault = cursor.readUncommittedDefault;
190         serializableIsolationDefault = cursor.serializableIsolationDefault;
191         updateOperationsProhibited = cursor.updateOperationsProhibited;
192
193         cursorImpl = cursor.cursorImpl.dup(samePosition);
194         dbImpl = cursor.dbImpl;
195         dbHandle = cursor.dbHandle;
196         if (dbHandle != null) {
197             dbHandle.addCursor(this);
198         }
199         config = cursor.config;
200     logger = dbImpl.getDbEnvironment().getLogger();
201     }
202
203     /**
204      * Internal entrypoint.
205      */

206     CursorImpl getCursorImpl() {
207         return cursorImpl;
208     }
209
210     /**
211      * Javadoc for this public method is generated via
212      * the doc templates in the doc_src directory.
213      */

214     public Database getDatabase() {
215     return dbHandle;
216     }
217
218     /**
219      * Always returns non-null, while getDatabase() returns null if no handle
220      * is associated with this cursor.
221      */

222     DatabaseImpl getDatabaseImpl() {
223     return dbImpl;
224     }
225
226     /**
227      * Javadoc for this public method is generated via
228      * the doc templates in the doc_src directory.
229      */

230     public CursorConfig getConfig() {
231     try {
232         return config.cloneConfig();
233     } catch (Error JavaDoc E) {
234         dbImpl.getDbEnvironment().invalidate(E);
235         throw E;
236     }
237     }
238
239     void setNonCloning(boolean nonCloning) {
240     cursorImpl.setNonCloning(nonCloning);
241     }
242
243     /**
244      * Javadoc for this public method is generated via
245      * the doc templates in the doc_src directory.
246      */

247     public synchronized void close()
248         throws DatabaseException {
249
250     try {
251         checkState(false);
252         cursorImpl.close();
253         if (dbHandle != null) {
254         dbHandle.removeCursor(this);
255         }
256     } catch (Error JavaDoc E) {
257         dbImpl.getDbEnvironment().invalidate(E);
258         throw E;
259     }
260     }
261
262     /**
263      * Javadoc for this public method is generated via
264      * the doc templates in the doc_src directory.
265      */

266     public int count()
267         throws DatabaseException {
268         
269         checkState(true);
270         trace(Level.FINEST, "Cursor.count: ", null);
271
272         /*
273          * Specify a null LockMode to use default locking. The API doesn't
274          * allow specifying a lock mode, but we should at least honor the
275          * configured default.
276          */

277         return countInternal(null);
278     }
279
280     /**
281      * Javadoc for this public method is generated via
282      * the doc templates in the doc_src directory.
283      */

284     public Cursor dup(boolean samePosition)
285         throws DatabaseException {
286
287     try {
288         checkState(false);
289         return new Cursor(this, samePosition);
290     } catch (Error JavaDoc E) {
291         dbImpl.getDbEnvironment().invalidate(E);
292         throw E;
293     }
294     }
295
296     /**
297      * Javadoc for this public method is generated via
298      * the doc templates in the doc_src directory.
299      */

300     public OperationStatus delete()
301         throws DatabaseException {
302
303         checkState(true);
304         checkUpdatesAllowed("delete");
305         trace(Level.FINEST, "Cursor.delete: ", null);
306
307         return deleteInternal();
308     }
309
310     /**
311      * Javadoc for this public method is generated via
312      * the doc templates in the doc_src directory.
313      */

314     public OperationStatus put(DatabaseEntry key, DatabaseEntry data)
315         throws DatabaseException {
316
317         checkState(false);
318         DatabaseUtil.checkForNullDbt(key, "key", true);
319         DatabaseUtil.checkForNullDbt(data, "data", true);
320         DatabaseUtil.checkForPartialKey(key);
321         checkUpdatesAllowed("put");
322         trace(Level.FINEST, "Cursor.put: ", key, data, null);
323
324         return putInternal(key, data, PutMode.OVERWRITE);
325     }
326
327     /**
328      * Javadoc for this public method is generated via
329      * the doc templates in the doc_src directory.
330      */

331     public OperationStatus putNoOverwrite(DatabaseEntry key,
332                                           DatabaseEntry data)
333         throws DatabaseException {
334
335         checkState(false);
336         DatabaseUtil.checkForNullDbt(key, "key", true);
337         DatabaseUtil.checkForNullDbt(data, "data", true);
338         DatabaseUtil.checkForPartialKey(key);
339         checkUpdatesAllowed("putNoOverwrite");
340         trace(Level.FINEST, "Cursor.putNoOverwrite: ", key, data, null);
341
342         return putInternal(key, data, PutMode.NOOVERWRITE);
343     }
344
345     /**
346      * Javadoc for this public method is generated via
347      * the doc templates in the doc_src directory.
348      */

349     public OperationStatus putNoDupData(DatabaseEntry key, DatabaseEntry data)
350         throws DatabaseException {
351
352         checkState(false);
353         DatabaseUtil.checkForNullDbt(key, "key", true);
354         DatabaseUtil.checkForNullDbt(data, "data", true);
355         DatabaseUtil.checkForPartialKey(key);
356         checkUpdatesAllowed("putNoDupData");
357         trace(Level.FINEST, "Cursor.putNoDupData: ", key, data, null);
358
359         return putInternal(key, data, PutMode.NODUP);
360     }
361
362     /**
363      * Javadoc for this public method is generated via
364      * the doc templates in the doc_src directory.
365      */

366     public OperationStatus putCurrent(DatabaseEntry data)
367         throws DatabaseException {
368
369         checkState(true);
370         DatabaseUtil.checkForNullDbt(data, "data", true);
371         checkUpdatesAllowed("putCurrent");
372         trace(Level.FINEST, "Cursor.putCurrent: ", null, data, null);
373
374         return putInternal(null, data, PutMode.CURRENT);
375     }
376
377     /**
378      * Javadoc for this public method is generated via
379      * the doc templates in the doc_src directory.
380      */

381     public OperationStatus getCurrent(DatabaseEntry key,
382                                       DatabaseEntry data,
383                                       LockMode lockMode)
384         throws DatabaseException {
385
386     try {
387         checkState(true);
388         checkArgsNoValRequired(key, data);
389         trace(Level.FINEST, "Cursor.getCurrent: ", lockMode);
390
391         return getCurrentInternal(key, data, lockMode);
392     } catch (Error JavaDoc E) {
393         dbImpl.getDbEnvironment().invalidate(E);
394         throw E;
395     }
396     }
397
398     /**
399      * Javadoc for this public method is generated via
400      * the doc templates in the doc_src directory.
401      */

402     public OperationStatus getFirst(DatabaseEntry key,
403                                     DatabaseEntry data,
404                                     LockMode lockMode)
405         throws DatabaseException {
406
407         checkState(false);
408         checkArgsNoValRequired(key, data);
409         trace(Level.FINEST, "Cursor.getFirst: ",lockMode);
410
411         return position(key, data, lockMode, true);
412     }
413
414     /**
415      * Javadoc for this public method is generated via
416      * the doc templates in the doc_src directory.
417      */

418     public OperationStatus getLast(DatabaseEntry key,
419                                    DatabaseEntry data,
420                                    LockMode lockMode)
421         throws DatabaseException {
422
423         checkState(false);
424         checkArgsNoValRequired(key, data);
425         trace(Level.FINEST, "Cursor.getLast: ", lockMode);
426
427         return position(key, data, lockMode, false);
428     }
429
430     /**
431      * Javadoc for this public method is generated via
432      * the doc templates in the doc_src directory.
433      */

434     public OperationStatus getNext(DatabaseEntry key,
435                                    DatabaseEntry data,
436                                    LockMode lockMode)
437         throws DatabaseException {
438
439         checkState(false);
440         checkArgsNoValRequired(key, data);
441         trace(Level.FINEST, "Cursor.getNext: ", lockMode);
442
443         if (cursorImpl.isNotInitialized()) {
444             return position(key, data, lockMode, true);
445         } else {
446             return retrieveNext(key, data, lockMode, GetMode.NEXT);
447         }
448     }
449
450     /**
451      * Javadoc for this public method is generated via
452      * the doc templates in the doc_src directory.
453      */

454     public OperationStatus getNextDup(DatabaseEntry key,
455                                       DatabaseEntry data,
456                                       LockMode lockMode)
457         throws DatabaseException {
458
459         checkState(true);
460         checkArgsNoValRequired(key, data);
461         trace(Level.FINEST, "Cursor.getNextDup: ", lockMode);
462
463         return retrieveNext(key, data, lockMode, GetMode.NEXT_DUP);
464     }
465
466     /**
467      * Javadoc for this public method is generated via
468      * the doc templates in the doc_src directory.
469      */

470     public OperationStatus getNextNoDup(DatabaseEntry key,
471                                         DatabaseEntry data,
472                                         LockMode lockMode)
473         throws DatabaseException {
474
475         checkState(false);
476         checkArgsNoValRequired(key, data);
477         trace(Level.FINEST, "Cursor.getNextNoDup: ", lockMode);
478
479         if (cursorImpl.isNotInitialized()) {
480             return position(key, data, lockMode, true);
481         } else {
482             return retrieveNext(key, data, lockMode, GetMode.NEXT_NODUP);
483         }
484     }
485
486     /**
487      * Javadoc for this public method is generated via
488      * the doc templates in the doc_src directory.
489      */

490     public OperationStatus getPrev(DatabaseEntry key,
491                                    DatabaseEntry data,
492                                    LockMode lockMode)
493         throws DatabaseException {
494
495         checkState(false);
496         checkArgsNoValRequired(key, data);
497         trace(Level.FINEST, "Cursor.getPrev: ", lockMode);
498
499         if (cursorImpl.isNotInitialized()) {
500             return position(key, data, lockMode, false);
501         } else {
502             return retrieveNext(key, data, lockMode, GetMode.PREV);
503         }
504     }
505
506     /**
507      * Javadoc for this public method is generated via
508      * the doc templates in the doc_src directory.
509      */

510     public OperationStatus getPrevDup(DatabaseEntry key,
511                                       DatabaseEntry data,
512                                       LockMode lockMode)
513         throws DatabaseException {
514
515         checkState(true);
516         checkArgsNoValRequired(key, data);
517         trace(Level.FINEST, "Cursor.getPrevDup: ", lockMode);
518
519         return retrieveNext(key, data, lockMode, GetMode.PREV_DUP);
520     }
521
522     /**
523      * Javadoc for this public method is generated via
524      * the doc templates in the doc_src directory.
525      */

526     public OperationStatus getPrevNoDup(DatabaseEntry key,
527                                         DatabaseEntry data,
528                                         LockMode lockMode)
529         throws DatabaseException {
530
531         checkState(false);
532         checkArgsNoValRequired(key, data);
533         trace(Level.FINEST, "Cursor.getPrevNoDup: ", lockMode);
534
535         if (cursorImpl.isNotInitialized()) {
536             return position(key, data, lockMode, false);
537         } else {
538             return retrieveNext(key, data, lockMode, GetMode.PREV_NODUP);
539         }
540     }
541
542     /**
543      * Javadoc for this public method is generated via
544      * the doc templates in the doc_src directory.
545      */

546     public OperationStatus getSearchKey(DatabaseEntry key,
547                                         DatabaseEntry data,
548                                         LockMode lockMode)
549         throws DatabaseException {
550
551         checkState(false);
552         DatabaseUtil.checkForNullDbt(key, "key", true);
553         DatabaseUtil.checkForNullDbt(data, "data", false);
554         trace(Level.FINEST, "Cursor.getSearchKey: ", key, null, lockMode);
555
556         return search(key, data, lockMode, SearchMode.SET);
557     }
558
559     /**
560      * Javadoc for this public method is generated via
561      * the doc templates in the doc_src directory.
562      */

563     public OperationStatus getSearchKeyRange(DatabaseEntry key,
564                                              DatabaseEntry data,
565                                              LockMode lockMode)
566         throws DatabaseException {
567
568         checkState(false);
569         DatabaseUtil.checkForNullDbt(key, "key", true);
570         DatabaseUtil.checkForNullDbt(data, "data", false);
571         trace(Level.FINEST, "Cursor.getSearchKeyRange: ", key, null, lockMode);
572
573         return search(key, data, lockMode, SearchMode.SET_RANGE);
574     }
575
576     /**
577      * Javadoc for this public method is generated via
578      * the doc templates in the doc_src directory.
579      */

580     public OperationStatus getSearchBoth(DatabaseEntry key,
581                                          DatabaseEntry data,
582                                          LockMode lockMode)
583         throws DatabaseException {
584
585         checkState(false);
586         checkArgsValRequired(key, data);
587         trace(Level.FINEST, "Cursor.getSearchBoth: ", key, data, lockMode);
588
589         return search(key, data, lockMode, SearchMode.BOTH);
590     }
591
592     /**
593      * Javadoc for this public method is generated via
594      * the doc templates in the doc_src directory.
595      */

596     public OperationStatus getSearchBothRange(DatabaseEntry key,
597                                               DatabaseEntry data,
598                                               LockMode lockMode)
599         throws DatabaseException {
600
601         checkState(false);
602         checkArgsValRequired(key, data);
603         trace(Level.FINEST, "Cursor.getSearchBothRange: ", key, data,
604               lockMode);
605
606         return search(key, data, lockMode, SearchMode.BOTH_RANGE);
607     }
608
609     /**
610      * Counts duplicates without parameter checking.
611      */

612     int countInternal(LockMode lockMode)
613         throws DatabaseException {
614         
615     try {
616         CursorImpl original = null;
617         CursorImpl dup = null;
618
619         /*
620          * We depart from the usual beginRead/endRead sequence because
621          * count() should not retain locks unless transactions are used.
622          * Therefore we always close the dup cursor after using it.
623          */

624         try {
625         original = cursorImpl;
626         dup = original.cloneCursor(true);
627         return dup.count(getLockType(lockMode, false));
628         } finally {
629         if (dup != original &&
630             dup != null) {
631             dup.close();
632         }
633         }
634     } catch (Error JavaDoc E) {
635         dbImpl.getDbEnvironment().invalidate(E);
636         throw E;
637     }
638     }
639
640     /**
641      * Internal version of delete() that does no parameter checking. Calls
642      * deleteNoNotify() and notifies triggers (performs secondary updates).
643      */

644     OperationStatus deleteInternal()
645         throws DatabaseException {
646
647     try {
648         /* Get existing data if updating secondaries. */
649         DatabaseEntry oldKey = null;
650         DatabaseEntry oldData = null;
651         boolean doNotifyTriggers =
652         dbHandle != null && dbHandle.hasTriggers();
653         if (doNotifyTriggers) {
654         oldKey = new DatabaseEntry();
655         oldData = new DatabaseEntry();
656         OperationStatus status = getCurrentInternal(oldKey, oldData,
657                                 LockMode.RMW);
658         if (status != OperationStatus.SUCCESS) {
659             return OperationStatus.KEYEMPTY;
660         }
661         }
662
663         /*
664          * Notify triggers before the actual deletion so that a primary
665          * record never exists while secondary keys refer to it. This is
666          * relied on by secondary read-uncommitted.
667          */

668         if (doNotifyTriggers) {
669         dbHandle.notifyTriggers(cursorImpl.getLocker(),
670                     oldKey, oldData, null);
671         }
672
673         /* The actual deletion. */
674         OperationStatus status = deleteNoNotify();
675         return status;
676     } catch (Error JavaDoc E) {
677         dbImpl.getDbEnvironment().invalidate(E);
678         throw E;
679     }
680     }
681
682     /**
683      * Clone the cursor, delete at current position, and if successful, swap
684      * cursors. Does not notify triggers (does not perform secondary updates).
685      */

686     OperationStatus deleteNoNotify()
687         throws DatabaseException {
688
689         CursorImpl original = null;
690         CursorImpl dup = null;
691     OperationStatus status = OperationStatus.KEYEMPTY;
692         try {
693             /* Clone, add dup to cursor. */
694             original = cursorImpl;
695             dup = original.cloneCursor(true);
696
697             /* Latch the bins and do the delete with the dup. */
698             dup.latchBINs();
699             status = dup.delete();
700
701             return status;
702         } finally {
703             if (original != null) {
704                 original.releaseBINs();
705             }
706             if (dup != null) {
707                 dup.releaseBINs();
708             }
709
710             /* Swap if it was a success. */
711         boolean success = (status == OperationStatus.SUCCESS);
712         if (cursorImpl == dup) {
713         if (!success) {
714             cursorImpl.reset();
715         }
716         } else {
717         if (success) {
718             original.close();
719             cursorImpl = dup;
720         } else {
721             dup.close();
722         }
723         }
724         }
725     }
726
727     /**
728      * Internal version of put() that does no parameter checking. Calls
729      * putNoNotify() and notifies triggers (performs secondary updates).
730      * Prevents phantoms.
731      */

732     OperationStatus putInternal(DatabaseEntry key,
733                 DatabaseEntry data,
734                                 PutMode putMode)
735         throws DatabaseException {
736
737     try {
738         /* Need to get existing data if updating secondaries. */
739         DatabaseEntry oldData = null;
740         boolean doNotifyTriggers =
741         dbHandle != null && dbHandle.hasTriggers();
742         if (doNotifyTriggers && (putMode == PutMode.CURRENT ||
743                      putMode == PutMode.OVERWRITE)) {
744         oldData = new DatabaseEntry();
745         if (key == null && putMode == PutMode.CURRENT) {
746             /* Key is returned by CursorImpl.putCurrent as foundKey. */
747             key = new DatabaseEntry();
748         }
749         }
750
751         /* Perform put. */
752         OperationStatus commitStatus =
753         putNoNotify(key, data, putMode, oldData);
754
755         /* Notify triggers (update secondaries). */
756         if (doNotifyTriggers && commitStatus == OperationStatus.SUCCESS) {
757         if (oldData != null && oldData.getData() == null) {
758             oldData = null;
759         }
760         dbHandle.notifyTriggers(cursorImpl.getLocker(), key,
761                     oldData, data);
762         }
763         return commitStatus;
764     } catch (Error JavaDoc E) {
765         dbImpl.getDbEnvironment().invalidate(E);
766         throw E;
767     }
768     }
769
770     /**
771      * Performs the put operation but does not notify triggers (does not
772      * perform secondary updates). Prevents phantoms.
773      */

774     OperationStatus putNoNotify(DatabaseEntry key,
775                 DatabaseEntry data,
776                                 PutMode putMode,
777                                 DatabaseEntry returnOldData)
778         throws DatabaseException {
779
780         Locker nextKeyLocker = null;
781         CursorImpl nextKeyCursor = null;
782         try {
783             /* If other transactions are serializable, lock the next key. */
784             Locker cursorLocker = cursorImpl.getLocker();
785             if (putMode != PutMode.CURRENT &&
786                 dbImpl.getDbEnvironment()
787                       .getTxnManager()
788                       .areOtherSerializableTransactionsActive(cursorLocker)) {
789                 nextKeyLocker = new BuddyLocker
790                     (dbImpl.getDbEnvironment(), cursorLocker);
791                 nextKeyCursor = new CursorImpl(dbImpl, nextKeyLocker);
792                 /* Perform eviction for user cursors. */
793                 nextKeyCursor.setAllowEviction(true);
794                 nextKeyCursor.lockNextKeyForInsert(key, data);
795             }
796
797             /* Perform the put operation. */
798             return putAllowPhantoms
799                 (key, data, putMode, returnOldData, nextKeyCursor);
800         } finally {
801             /* Release the next-key lock. */
802             if (nextKeyCursor != null) {
803                 nextKeyCursor.close();
804             }
805             if (nextKeyLocker != null) {
806                 nextKeyLocker.operationEnd();
807             }
808         }
809     }
810
811     /**
812      * Clone the cursor, put key/data according to PutMode, and if successful,
813      * swap cursors. Does not notify triggers (does not perform secondary
814      * updates). Does not prevent phantoms.
815      *
816      * @param nextKeyCursor is the cursor used to lock the next key during
817      * phantom prevention. If this cursor is non-null and initialized, it's
818      * BIN will be used to initialize the dup cursor used to perform insertion.
819      * This enables an optimization that skips the search for the BIN.
820      */

821     private OperationStatus putAllowPhantoms(DatabaseEntry key,
822                                              DatabaseEntry data,
823                                              PutMode putMode,
824                                              DatabaseEntry returnOldData,
825                                              CursorImpl nextKeyCursor)
826         throws DatabaseException {
827
828         if (data == null) {
829             throw new NullPointerException JavaDoc
830                 ("put passed a null DatabaseEntry arg");
831         }
832
833         if (putMode != PutMode.CURRENT && key == null) {
834             throw new IllegalArgumentException JavaDoc
835                 ("put passed a null DatabaseEntry arg");
836         }
837
838         CursorImpl original = null;
839         OperationStatus status = OperationStatus.NOTFOUND;
840         CursorImpl dup = null;
841         try {
842             /* Latch and clone. */
843             original = cursorImpl;
844
845             if (putMode == PutMode.CURRENT) {
846                 /* Call addCursor for putCurrent. */
847                 dup = original.cloneCursor(true);
848             } else {
849
850                 /*
851                  * Do not call addCursor when inserting. Copy the position of
852                  * nextKeyCursor if available.
853                  */

854                 dup = original.cloneCursor(false, nextKeyCursor);
855             }
856
857             /* Perform operation. */
858             if (putMode == PutMode.CURRENT) {
859                 status = dup.putCurrent(data, key, returnOldData);
860             } else if (putMode == PutMode.OVERWRITE) {
861                 status = dup.put(key, data, returnOldData);
862             } else if (putMode == PutMode.NOOVERWRITE) {
863                 status = dup.putNoOverwrite(key, data);
864             } else if (putMode == PutMode.NODUP) {
865                 status = dup.putNoDupData(key, data);
866             } else {
867                 throw new InternalException("unknown PutMode");
868             }
869                     
870             return status;
871         } finally {
872             if (original != null) {
873                 original.releaseBINs();
874             }
875
876         boolean success = (status == OperationStatus.SUCCESS);
877         if (cursorImpl == dup) {
878         if (!success) {
879             cursorImpl.reset();
880         }
881         } else {
882         if (success) {
883             original.close();
884             cursorImpl = dup;
885         } else {
886             if (dup != null) {
887             dup.close();
888             }
889         }
890         }
891         }
892     }
893
894     /**
895      * Position the cursor at the first or last record of the database.
896      * Prevents phantoms.
897      */

898     OperationStatus position(DatabaseEntry key,
899                              DatabaseEntry data,
900                              LockMode lockMode,
901                              boolean first)
902         throws DatabaseException {
903
904     try {
905         if (!isSerializableIsolation(lockMode)) {
906         return positionAllowPhantoms
907             (key, data, getLockType(lockMode, false), first);
908         }
909
910         /*
911          * Perform range locking to prevent phantoms and handle restarts.
912          */

913         while (true) {
914         try {
915             /* Range lock the EOF node before getLast. */
916             if (!first) {
917             cursorImpl.lockEofNode(LockType.RANGE_READ);
918             }
919
920             /* Use a range lock for getFirst. */
921             LockType lockType = getLockType(lockMode, first);
922
923             /* Perform operation. */
924             OperationStatus status =
925             positionAllowPhantoms(key, data, lockType, first);
926
927             /*
928              * Range lock the EOF node when getFirst returns NOTFOUND.
929              */

930             if (first && status != OperationStatus.SUCCESS) {
931             cursorImpl.lockEofNode(LockType.RANGE_READ);
932             }
933
934             return status;
935         } catch (RangeRestartException e) {
936             continue;
937         }
938         }
939     } catch (Error JavaDoc E) {
940         dbImpl.getDbEnvironment().invalidate(E);
941         throw E;
942     }
943     }
944
945     /**
946      * Position without preventing phantoms.
947      */

948     private OperationStatus positionAllowPhantoms(DatabaseEntry key,
949                                                   DatabaseEntry data,
950                                                   LockType lockType,
951                                                   boolean first)
952         throws DatabaseException {
953
954         assert (key != null && data != null);
955
956         OperationStatus status = OperationStatus.NOTFOUND;
957         CursorImpl dup = null;
958         try {
959
960             /*
961              * Pass false: no need to call addCursor here because
962              * CursorImpl.position will be adding it after it finds the bin.
963              */

964             dup = beginRead(false);
965
966             /* Search for first or last. */
967             if (!dup.positionFirstOrLast(first, null)) {
968                 /* Tree is empty. */
969                 status = OperationStatus.NOTFOUND;
970                 assert LatchSupport.countLatchesHeld() == 0:
971                     LatchSupport.latchesHeldToString();
972
973             } else {
974                 /* Found something in this tree. */
975                 assert LatchSupport.countLatchesHeld() == 1:
976                     LatchSupport.latchesHeldToString();
977                 status = dup.getCurrentAlreadyLatched
978                     (key, data, lockType, first);
979
980                 if (status == OperationStatus.SUCCESS) {
981             if (dup.getDupBIN() != null) {
982             dup.incrementLNCount();
983             }
984                 } else {
985                     /* The record we're pointing at may be deleted. */
986                     status = dup.getNext(key, data, lockType, first, false);
987         }
988             }
989         } finally {
990
991             /*
992              * positionFirstOrLast returns with the target BIN latched, so it
993              * is the responsibility of this method to make sure the latches
994              * are released.
995              */

996             cursorImpl.releaseBINs();
997             endRead(dup, status == OperationStatus.SUCCESS);
998         }
999         return status;
1000    }
1001
1002    /**
1003     * Perform search by key, data, or both. Prevents phantoms.
1004     */

1005    OperationStatus search(DatabaseEntry key,
1006                           DatabaseEntry data,
1007                           LockMode lockMode,
1008                           SearchMode searchMode)
1009        throws DatabaseException {
1010
1011    try {
1012        if (!isSerializableIsolation(lockMode)) {
1013        LockType lockType = getLockType(lockMode, false);
1014        KeyChangeStatus result = searchAllowPhantoms
1015            (key, data, lockType, lockType, searchMode);
1016        return result.status;
1017        }
1018
1019        /*
1020         * Perform range locking to prevent phantoms and handle restarts.
1021         */

1022        while (true) {
1023        try {
1024            /* Do not use a range lock for the initial search. */
1025            LockType searchLockType = getLockType(lockMode, false);
1026
1027            /* Switch to a range lock when advancing forward. */
1028            LockType advanceLockType = getLockType(lockMode, true);
1029
1030            /* Do not modify key/data params until SUCCESS. */
1031            DatabaseEntry tryKey = new DatabaseEntry
1032            (key.getData(), key.getOffset(), key.getSize());
1033            DatabaseEntry tryData = new DatabaseEntry
1034            (data.getData(), data.getOffset(), data.getSize());
1035            KeyChangeStatus result;
1036
1037            if (searchMode.isExactSearch()) {
1038
1039            /*
1040             * Artificial range search to range lock the next key.
1041             */

1042            result = searchExactAndRangeLock
1043                (tryKey, tryData, searchLockType, advanceLockType,
1044                 searchMode);
1045            } else {
1046            /* Normal range search. */
1047            result = searchAllowPhantoms
1048                (tryKey, tryData, searchLockType, advanceLockType,
1049                 searchMode);
1050
1051            /* Lock the EOF node if no records follow the key. */
1052            if (result.status != OperationStatus.SUCCESS) {
1053                cursorImpl.lockEofNode(LockType.RANGE_READ);
1054            }
1055            }
1056
1057            /*
1058             * Only overwrite key/data on SUCCESS, after all locking.
1059             */

1060            if (result.status == OperationStatus.SUCCESS) {
1061            key.setData(tryKey.getData(), 0, tryKey.getSize());
1062            data.setData(tryData.getData(), 0, tryData.getSize());
1063            }
1064
1065            return result.status;
1066        } catch (RangeRestartException e) {
1067            continue;
1068        }
1069        }
1070    } catch (Error JavaDoc E) {
1071        dbImpl.getDbEnvironment().invalidate(E);
1072        throw E;
1073    }
1074    }
1075
1076    /**
1077     * For an exact search, perform a range search and return NOTFOUND if the
1078     * key changes (or if the data changes for BOTH) during the search.
1079     * If no exact match is found the range search will range lock the
1080     * following key for phantom prevention. Importantly, the cursor position
1081     * is not changed if an exact match is not found, even though we advance to
1082     * the following key in order to range lock it.
1083     */

1084    private KeyChangeStatus searchExactAndRangeLock(DatabaseEntry key,
1085                                                    DatabaseEntry data,
1086                                                    LockType searchLockType,
1087                                                    LockType advanceLockType,
1088                                                    SearchMode searchMode)
1089        throws DatabaseException {
1090
1091        /* Convert exact search to range search. */
1092        searchMode = (searchMode == SearchMode.SET) ?
1093            SearchMode.SET_RANGE : SearchMode.BOTH_RANGE;
1094
1095        KeyChangeStatus result = null;
1096        boolean noNextKeyFound;
1097
1098        CursorImpl dup =
1099            beginRead(false /* searchAndPosition will add cursor */);
1100
1101        try {
1102
1103            /*
1104             * Perform a range search and return NOTFOUND if an exact match is
1105             * not found. Pass advanceAfterRangeSearch=true to advance even if
1106             * the key is not matched, to lock the following key.
1107             */

1108            result = searchInternal
1109                (dup, key, data, searchLockType, advanceLockType, searchMode,
1110                 true /*advanceAfterRangeSearch*/);
1111
1112            /* The keyChange value is independent of the status value. */
1113            noNextKeyFound = !result.keyChange;
1114
1115            /* If the key changed, then we do not have an exact match. */
1116            if (result.keyChange && result.status == OperationStatus.SUCCESS) {
1117                result.status = OperationStatus.NOTFOUND;
1118            }
1119        } finally {
1120            endRead(dup, result != null &&
1121                         result.status == OperationStatus.SUCCESS);
1122        }
1123
1124        /* Lock the EOF node if no more records, whether or not more dups. */
1125        if (noNextKeyFound) {
1126            cursorImpl.lockEofNode(LockType.RANGE_READ);
1127        }
1128
1129        return result;
1130    }
1131
1132    /**
1133     * Perform search without preventing phantoms.
1134     */

1135    private KeyChangeStatus searchAllowPhantoms(DatabaseEntry key,
1136                                                DatabaseEntry data,
1137                                                LockType searchLockType,
1138                                                LockType advanceLockType,
1139                                                SearchMode searchMode)
1140        throws DatabaseException {
1141
1142        OperationStatus status = OperationStatus.NOTFOUND;
1143
1144        CursorImpl dup =
1145            beginRead(false /* searchAndPosition will add cursor */);
1146
1147        try {
1148            KeyChangeStatus result = searchInternal
1149                (dup, key, data, searchLockType, advanceLockType, searchMode,
1150                 false /*advanceAfterRangeSearch*/);
1151
1152            status = result.status;
1153            return result;
1154        } finally {
1155            endRead(dup, status == OperationStatus.SUCCESS);
1156        }
1157    }
1158
1159    /**
1160     * Perform search for a given CursorImpl.
1161     */

1162    private KeyChangeStatus searchInternal(CursorImpl dup,
1163                                           DatabaseEntry key,
1164                                           DatabaseEntry data,
1165                                           LockType searchLockType,
1166                                           LockType advanceLockType,
1167                                           SearchMode searchMode,
1168                                           boolean advanceAfterRangeSearch)
1169        throws DatabaseException {
1170
1171        assert key != null && data != null;
1172
1173        OperationStatus status = OperationStatus.NOTFOUND;
1174        boolean keyChange = false;
1175
1176        try {
1177            /* search */
1178            int searchResult =
1179                dup.searchAndPosition(key, data, searchMode, searchLockType);
1180            if ((searchResult & CursorImpl.FOUND) != 0) {
1181
1182                /*
1183                 * The search found a possibly valid record.
1184                 * CursorImpl.searchAndPosition's job is to settle the cursor
1185                 * at a particular location on a BIN. In some cases, the
1186                 * current position may not actually hold a valid record, so
1187                 * it's this layer's responsiblity to judge if it might need to
1188                 * bump the cursor along and search more. For example, we might
1189                 * have to do so if the position holds a deleted record.
1190         *
1191                 * Advance the cursor if:
1192                 *
1193                 * 1. This is a range type search and there was no match on the
1194                 * search criteria (the key or key and data depending on the
1195                 * type of search). Then we search forward until there's a
1196                 * match.
1197                 *
1198                 * 2. If this is not a range type search, check the record at
1199                 * the current position. If this is not a duplicate set,
1200                 * CursorImpl.searchAndPosition gave us an exact answer.
1201                 * However since it doesn't peer into the duplicate set, we may
1202                 * need to probe further in if there are deleted records in the
1203                 * duplicate set. i.e, we have to be able to find k1/d2 even if
1204                 * there's k1/d1(deleted), k1/d2, k1/d3, etc in a duplicate
1205                 * set.
1206         *
1207         * Note that searchResult has four bits possibly set:
1208                 *
1209                 * FOUND has already been checked above.
1210                 *
1211         * EXACT_KEY means an exact match on the key portion was made.
1212                 *
1213         * EXACT_DATA means that if searchMode was BOTH or BOTH_RANGE
1214         * then an exact match was made on the data (in addition to the
1215         * key).
1216                 *
1217                 * FOUND_LAST means that the cursor is positioned at the last
1218                 * record in the database.
1219                 */

1220        boolean exactKeyMatch =
1221            ((searchResult & CursorImpl.EXACT_KEY) != 0);
1222        boolean exactDataMatch =
1223            ((searchResult & CursorImpl.EXACT_DATA) != 0);
1224        boolean foundLast =
1225            ((searchResult & CursorImpl.FOUND_LAST) != 0);
1226
1227        /*
1228         * rangeMatch means that a range match of some sort (either
1229         * SET_RANGE or BOTH_RANGE) was specified and there wasn't a
1230                 * complete match. If SET_RANGE was spec'd and EXACT_KEY was
1231                 * not returned as set, then the key didn't match exactly. If
1232                 * BOTH_RANGE was spec'd and EXACT_DATA was not returned as
1233                 * set, then the data didn't match exactly.
1234         */

1235        boolean rangeMatch = false;
1236        if (searchMode == SearchMode.SET_RANGE &&
1237            !exactKeyMatch) {
1238            rangeMatch = true;
1239        }
1240
1241        if (searchMode == SearchMode.BOTH_RANGE &&
1242            (!exactKeyMatch || !exactDataMatch)) {
1243            rangeMatch = true;
1244        }
1245
1246                /*
1247                 * Pass null for key to getCurrentAlreadyLatched if searchMode
1248                 * is SET since the key is not supposed to be set in that case.
1249                 */

1250                DatabaseEntry useKey =
1251                    (searchMode == SearchMode.SET) ?
1252                    null : key;
1253
1254        /*
1255         * rangeMatch => an exact match was not found so we need to
1256         * advance the cursor to a real item using getNextXXX. If
1257         * rangeMatch is true, then cursor is currently on some entry,
1258         * but that entry is either deleted or is prior to the target
1259                 * key/data. It is also possible that rangeMatch is false (we
1260                 * have an exact match) but the entry is deleted. So we test
1261                 * for rangeMatch or a deleted entry, and if either is true
1262                 * then we advance to the next non-deleted entry.
1263         */

1264                if (rangeMatch ||
1265                    (status = dup.getCurrentAlreadyLatched
1266                     (useKey, data, searchLockType, true)) ==
1267                    OperationStatus.KEYEMPTY) {
1268
1269                    if (foundLast) {
1270                        status = OperationStatus.NOTFOUND;
1271                    } else if (searchMode == SearchMode.SET) {
1272
1273                        /*
1274                         * SET is an exact operation, so this isn't a
1275                         * rangeMatch, it's a deleted record. We should
1276                         * advance, but only to duplicates for the same key.
1277                         */

1278                        status = dup.getNextDuplicate
1279                            (key, data, advanceLockType, true, rangeMatch);
1280                    } else if (searchMode == SearchMode.BOTH) {
1281
1282                        /*
1283                         * BOTH is also an exact operation, but we should not
1284                         * advance past a deleted record because the data match
1285                         * is exact. However, this API should return NOTFOUND
1286                         * instead of KEYEMPTY (which may be been set above).
1287                         */

1288                        if (status == OperationStatus.KEYEMPTY) {
1289                            status = OperationStatus.NOTFOUND;
1290                        }
1291                    } else {
1292                        assert !searchMode.isExactSearch();
1293
1294                        /* Save the search key for a BOTH_RANGE search. */
1295                        byte[] searchKey = null;
1296                        if (searchMode.isDataSearch()) {
1297                            searchKey = Key.makeKey(key);
1298                        }
1299
1300                        /*
1301                         * This may be a deleted record or a rangeMatch, and in
1302                         * either case we should advance. We must determine
1303                         * whether the key changes when we advance.
1304                         */

1305                        if (exactKeyMatch) {
1306                            KeyChangeStatus result =
1307                                dup.getNextWithKeyChangeStatus
1308                                (key, data, advanceLockType, true, rangeMatch);
1309                            status = result.status;
1310
1311                            /*
1312                             * For BOTH_RANGE, advancing always causes a data
1313                             * change, which is considered a key change. For
1314                             * SET_RANGE, getNextWithKeyChangeStatus determined
1315                             * the key change status.
1316                             */

1317                            keyChange = searchMode.isDataSearch() ?
1318                                (status == OperationStatus.SUCCESS) :
1319                                result.keyChange;
1320
1321                        } else if (searchMode.isDataSearch() &&
1322                                   !advanceAfterRangeSearch) {
1323
1324                            /*
1325                             * If we did not match the key (exactly) for
1326                             * BOTH_RANGE, and advanceAfterSearchRangeBoth is
1327                             * false, then return NOTFOUND.
1328                             */

1329                             status = OperationStatus.NOTFOUND;
1330                        } else {
1331
1332                            /*
1333                             * If we didn't match the key, skip over duplicates
1334                             * to the next key with getNextNoDup.
1335                             */

1336                            status = dup.getNextNoDup
1337                                (key, data, advanceLockType, true, rangeMatch);
1338                            
1339                            /* getNextNoDup always causes a key change. */
1340                            keyChange = (status == OperationStatus.SUCCESS);
1341                        }
1342
1343                        /*
1344                         * If we moved past the search key after a BOTH_RANGE
1345                         * search, return NOTFOUND. Leave the keyChange value
1346                         * intact, since we want to return this accurately
1347                         * regardless of the status return.
1348                         */

1349                        if (status == OperationStatus.SUCCESS &&
1350                            searchMode.isDataSearch()) {
1351                            if (Key.compareKeys
1352                                (key.getData(), searchKey,
1353                                 dbImpl.getBtreeComparator()) != 0) {
1354                                status = OperationStatus.NOTFOUND;
1355                            }
1356                        }
1357                    }
1358                }
1359            }
1360        } finally {
1361
1362            /*
1363             * searchAndPosition returns with the target BIN latched, so it is
1364             * the responsibility of this method to make sure the latches are
1365             * released.
1366             */

1367            cursorImpl.releaseBINs();
1368            if (status != OperationStatus.SUCCESS && dup != cursorImpl) {
1369                dup.releaseBINs();
1370            }
1371        }
1372        
1373        return new KeyChangeStatus(status, keyChange);
1374    }
1375
1376    /**
1377     * Retrieve the next or previous record. Prevents phantoms.
1378     */

1379    OperationStatus retrieveNext(DatabaseEntry key,
1380                                 DatabaseEntry data,
1381                                 LockMode lockMode,
1382                                 GetMode getMode)
1383        throws DatabaseException {
1384
1385    try {
1386        if (!isSerializableIsolation(lockMode)) {
1387        return retrieveNextAllowPhantoms
1388            (key, data, getLockType(lockMode, false), getMode);
1389        }
1390
1391        /*
1392         * Perform range locking to prevent phantoms and handle restarts.
1393         */

1394        while (true) {
1395        try {
1396            OperationStatus status;
1397            if (getMode == GetMode.NEXT_DUP) {
1398
1399            /*
1400             * Special case to lock the next key if no more dups.
1401             */

1402            status = getNextDupAndRangeLock(key, data, lockMode);
1403            } else {
1404
1405            /* Get a range lock for 'prev' operations. */
1406            if (!getMode.isForward()) {
1407                rangeLockCurrentPosition(getMode);
1408            }
1409
1410            /*
1411             * Use a range lock if performing a 'next' operation.
1412             */

1413            LockType lockType =
1414                getLockType(lockMode, getMode.isForward());
1415
1416            /* Perform the operation. */
1417            status = retrieveNextAllowPhantoms
1418                (key, data, lockType, getMode);
1419
1420            if (getMode.isForward() &&
1421                status != OperationStatus.SUCCESS) {
1422                /* NEXT, NEXT_NODUP: lock the EOF node. */
1423                cursorImpl.lockEofNode(LockType.RANGE_READ);
1424            }
1425            }
1426
1427            return status;
1428        } catch (RangeRestartException e) {
1429            continue;
1430        }
1431        }
1432    } catch (Error JavaDoc E) {
1433        dbImpl.getDbEnvironment().invalidate(E);
1434        throw E;
1435    }
1436    }
1437
1438    /**
1439     * Retrieve the next dup; if no next dup is found then range lock the
1440     * following key for phantom prevention. Importantly, the cursor position
1441     * is not changed if there are no more dups, even though we advance to the
1442     * following key in order to range lock it.
1443     */

1444    private OperationStatus getNextDupAndRangeLock(DatabaseEntry key,
1445                                                   DatabaseEntry data,
1446                                                   LockMode lockMode)
1447        throws DatabaseException {
1448
1449        /* Do not modify key/data params until SUCCESS. */
1450        DatabaseEntry tryKey = new DatabaseEntry();
1451        DatabaseEntry tryData = new DatabaseEntry();
1452
1453        /* Get a range lock. */
1454        LockType lockType = getLockType(lockMode, true);
1455        OperationStatus status;
1456        boolean noNextKeyFound;
1457
1458        /*
1459         * Perform a NEXT and return NOTFOUND if the key changes
1460         * during the search.
1461         */

1462        while (true) {
1463            assert LatchSupport.countLatchesHeld() == 0;
1464            CursorImpl dup = beginRead(true);
1465
1466        try {
1467                KeyChangeStatus result = dup.getNextWithKeyChangeStatus
1468                    (tryKey, tryData, lockType, true, false);
1469                status = result.status;
1470                noNextKeyFound = (status != OperationStatus.SUCCESS);
1471                if (result.keyChange && status == OperationStatus.SUCCESS) {
1472                    status = OperationStatus.NOTFOUND;
1473                }
1474        } catch (DatabaseException DBE) {
1475                endRead(dup, false);
1476        throw DBE;
1477        }
1478
1479            if (checkForInsertion(GetMode.NEXT, cursorImpl, dup)) {
1480                endRead(dup, false);
1481                continue;
1482            } else {
1483                endRead(dup, status == OperationStatus.SUCCESS);
1484                assert LatchSupport.countLatchesHeld() == 0;
1485                break;
1486            }
1487        }
1488
1489        /* Lock the EOF node if no more records, whether or not more dups. */
1490        if (noNextKeyFound) {
1491            cursorImpl.lockEofNode(LockType.RANGE_READ);
1492        }
1493
1494        /* Only overwrite key/data on SUCCESS. */
1495        if (status == OperationStatus.SUCCESS) {
1496            key.setData(tryKey.getData(), 0, tryKey.getSize());
1497            data.setData(tryData.getData(), 0, tryData.getSize());
1498        }
1499
1500        return status;
1501    }
1502
1503    /**
1504     * For 'prev' operations, upgrade to a range lock at the current position.
1505     * For PREV_NODUP, range lock the first duplicate instead. If there are no
1506     * records at the current position, get a range lock on the next record or,
1507     * if not found, on the logical EOF node. Do not modify the current
1508     * cursor position, use a separate cursor.
1509     */

1510    private void rangeLockCurrentPosition(GetMode getMode)
1511        throws DatabaseException {
1512
1513        DatabaseEntry tempKey = new DatabaseEntry();
1514        DatabaseEntry tempData = new DatabaseEntry();
1515        tempKey.setPartial(0, 0, true);
1516        tempData.setPartial(0, 0, true);
1517
1518        OperationStatus status;
1519        CursorImpl dup = cursorImpl.cloneCursor(true);
1520        try {
1521            if (getMode == GetMode.PREV_NODUP) {
1522                status = dup.getFirstDuplicate
1523                    (tempKey, tempData, LockType.RANGE_READ);
1524            } else {
1525                status = dup.getCurrent
1526                    (tempKey, tempData, LockType.RANGE_READ);
1527            }
1528            if (status != OperationStatus.SUCCESS) {
1529                while (true) {
1530                    assert LatchSupport.countLatchesHeld() == 0;
1531
1532                    status = dup.getNext
1533                        (tempKey, tempData, LockType.RANGE_READ, true, false);
1534
1535                    if (checkForInsertion(GetMode.NEXT, cursorImpl, dup)) {
1536                        dup.close();
1537                        dup = cursorImpl.cloneCursor(true);
1538                        continue;
1539                    } else {
1540                        assert LatchSupport.countLatchesHeld() == 0;
1541                        break;
1542                    }
1543                }
1544            }
1545        } finally {
1546        if (cursorImpl == dup) {
1547        dup.reset();
1548        } else {
1549        dup.close();
1550        }
1551        }
1552
1553        if (status != OperationStatus.SUCCESS) {
1554            cursorImpl.lockEofNode(LockType.RANGE_READ);
1555        }
1556    }
1557
1558    /**
1559     * Retrieve without preventing phantoms.
1560     */

1561    private OperationStatus retrieveNextAllowPhantoms(DatabaseEntry key,
1562                                                      DatabaseEntry data,
1563                                                      LockType lockType,
1564                                                      GetMode getMode)
1565        throws DatabaseException {
1566
1567        assert (key != null && data != null);
1568
1569        OperationStatus status;
1570
1571        while (true) {
1572            assert LatchSupport.countLatchesHeld() == 0;
1573            CursorImpl dup = beginRead(true);
1574
1575        try {
1576        if (getMode == GetMode.NEXT) {
1577            status = dup.getNext
1578                        (key, data, lockType, true, false);
1579        } else if (getMode == GetMode.PREV) {
1580            status = dup.getNext
1581                        (key, data, lockType, false, false);
1582        } else if (getMode == GetMode.NEXT_DUP) {
1583            status = dup.getNextDuplicate
1584                        (key, data, lockType, true, false);
1585        } else if (getMode == GetMode.PREV_DUP) {
1586            status = dup.getNextDuplicate
1587                        (key, data, lockType, false, false);
1588        } else if (getMode == GetMode.NEXT_NODUP) {
1589            status = dup.getNextNoDup
1590                        (key, data, lockType, true, false);
1591        } else if (getMode == GetMode.PREV_NODUP) {
1592            status = dup.getNextNoDup
1593                        (key, data, lockType, false, false);
1594        } else {
1595            throw new InternalException("unknown GetMode");
1596        }
1597        } catch (DatabaseException DBE) {
1598                endRead(dup, false);
1599        throw DBE;
1600        }
1601
1602            if (checkForInsertion(getMode, cursorImpl, dup)) {
1603                endRead(dup, false);
1604                continue;
1605            } else {
1606                endRead(dup, status == OperationStatus.SUCCESS);
1607                assert LatchSupport.countLatchesHeld() == 0;
1608                break;
1609            }
1610        }
1611        return status;
1612    }
1613
1614    /**
1615     * Returns the current key and data. There is no need to prevent phantoms.
1616     */

1617    OperationStatus getCurrentInternal(DatabaseEntry key,
1618                                       DatabaseEntry data,
1619                                       LockMode lockMode)
1620        throws DatabaseException {
1621
1622        /* Do not use a range lock. */
1623        LockType lockType = getLockType(lockMode, false);
1624
1625        return cursorImpl.getCurrent(key, data, lockType);
1626    }
1627
1628    /*
1629     * Something may have been added to the original cursor (cursorImpl) while
1630     * we were getting the next BIN. cursorImpl would have been adjusted
1631     * properly but we would have skipped a BIN in the process.
1632     *
1633     * Note that when we call LN.isDeleted(), we do not need to lock the LN.
1634     * If we see a non-committed deleted entry, we'll just iterate around in
1635     * the caller. So a false positive is ok.
1636     *
1637     * @return true if an unaccounted for insertion happened.
1638     */

1639    private boolean checkForInsertion(GetMode getMode,
1640                                      CursorImpl origCursor,
1641                                      CursorImpl dupCursor)
1642        throws DatabaseException {
1643
1644        BIN origBIN = origCursor.getBIN();
1645        BIN dupBIN = dupCursor.getBIN();
1646        DBIN origDBIN = origCursor.getDupBIN();
1647
1648        /* If fetchTarget returns null below, a deleted LN was cleaned. */
1649
1650        boolean forward = true;
1651        if (getMode == GetMode.PREV ||
1652            getMode == GetMode.PREV_DUP ||
1653            getMode == GetMode.PREV_NODUP) {
1654            forward = false;
1655        }
1656        boolean ret = false;
1657        if (origBIN != dupBIN) {
1658            /* We jumped to the next BIN during getNext(). */
1659            origCursor.latchBINs();
1660
1661        try {
1662        if (origDBIN == null) {
1663            if (forward) {
1664            if (origBIN.getNEntries() - 1 >
1665                origCursor.getIndex()) {
1666
1667                /*
1668                 * We were adjusted to something other than the
1669                 * last entry so some insertion happened.
1670                 */

1671                for (int i = origCursor.getIndex() + 1;
1672                 i < origBIN.getNEntries();
1673                 i++) {
1674                if (!origBIN.isEntryKnownDeleted(i)) {
1675                    Node n = origBIN.fetchTarget(i);
1676                    if (n != null && !n.containsDuplicates()) {
1677                    LN ln = (LN) n;
1678                    /* See comment above about locking. */
1679                    if (!ln.isDeleted()) {
1680                        ret = true;
1681                        break;
1682                    }
1683                    }
1684                } else {
1685                    /* Need to check the DupCountLN. */
1686                }
1687                }
1688            }
1689            } else {
1690            if (origCursor.getIndex() > 0) {
1691
1692                /*
1693                 * We were adjusted to something other than the
1694                 * first entry so some insertion happened.
1695                 */

1696                for (int i = 0; i < origCursor.getIndex(); i++) {
1697                if (!origBIN.isEntryKnownDeleted(i)) {
1698                    Node n = origBIN.fetchTarget(i);
1699                    if (n != null && !n.containsDuplicates()) {
1700                    LN ln = (LN) n;
1701                    /* See comment above about locking. */
1702                    if (!ln.isDeleted()) {
1703                        ret = true;
1704                        break;
1705                    }
1706                    } else {
1707                    /* Need to check the DupCountLN. */
1708                    }
1709                }
1710                }
1711            }
1712            }
1713        }
1714        } finally {
1715        origCursor.releaseBINs();
1716        }
1717            return ret;
1718        }
1719
1720        if (origDBIN != dupCursor.getDupBIN() &&
1721            origCursor.getIndex() == dupCursor.getIndex() &&
1722            getMode != GetMode.NEXT_NODUP &&
1723            getMode != GetMode.PREV_NODUP) {
1724            /* Same as above, only for the dupBIN. */
1725            origCursor.latchBINs();
1726        try {
1727        if (forward) {
1728            if (origDBIN.getNEntries() - 1 >
1729            origCursor.getDupIndex()) {
1730
1731            /*
1732             * We were adjusted to something other than the last
1733             * entry so some insertion happened.
1734             */

1735            for (int i = origCursor.getDupIndex() + 1;
1736                 i < origDBIN.getNEntries();
1737                 i++) {
1738                if (!origDBIN.isEntryKnownDeleted(i)) {
1739                Node n = origDBIN.fetchTarget(i);
1740                LN ln = (LN) n;
1741                /* See comment above about locking. */
1742                if (n != null && !ln.isDeleted()) {
1743                    ret = true;
1744                    break;
1745                }
1746                }
1747            }
1748            }
1749        } else {
1750            if (origCursor.getDupIndex() > 0) {
1751
1752            /*
1753             * We were adjusted to something other than the first
1754             * entry so some insertion happened.
1755             */

1756            for (int i = 0; i < origCursor.getDupIndex(); i++) {
1757                if (!origDBIN.isEntryKnownDeleted(i)) {
1758                Node n = origDBIN.fetchTarget(i);
1759                LN ln = (LN) n;
1760                /* See comment above about locking. */
1761                if (n != null && !ln.isDeleted()) {
1762                    ret = true;
1763                    break;
1764                }
1765                }
1766            }
1767            }
1768        }
1769        } finally {
1770        origCursor.releaseBINs();
1771        }
1772            return ret;
1773        }
1774        return false;
1775    }
1776
1777    /**
1778     * If the cursor is initialized, dup it and return the dup; otherwise,
1779     * return the original. This avoids the overhead of duping when the
1780     * original is uninitialized. The cursor returned must be passed to
1781     * endRead() to close the correct cursor.
1782     */

1783    private CursorImpl beginRead(boolean addCursor)
1784        throws DatabaseException {
1785
1786        CursorImpl dup;
1787        if (cursorImpl.isNotInitialized()) {
1788            dup = cursorImpl;
1789        } else {
1790            dup = cursorImpl.cloneCursor(addCursor);
1791        }
1792        return dup;
1793    }
1794
1795    /**
1796     * If the operation is successful, swaps cursors and closes the original
1797     * cursor; otherwise, closes the duped cursor. In the case where the
1798     * original cursor was not duped by beginRead because it was uninitialized,
1799     * just resets the original cursor if the operation did not succeed.
1800     */

1801    private void endRead(CursorImpl dup, boolean success)
1802        throws DatabaseException {
1803
1804        if (dup == cursorImpl) {
1805            if (!success) {
1806                cursorImpl.reset();
1807            }
1808        } else {
1809            if (success) {
1810                cursorImpl.close();
1811                cursorImpl = dup;
1812            } else {
1813        dup.close();
1814            }
1815        }
1816    }
1817
1818    boolean advanceCursor(DatabaseEntry key, DatabaseEntry data) {
1819    return cursorImpl.advanceCursor(key, data);
1820    }
1821
1822    private LockType getLockType(LockMode lockMode, boolean rangeLock) {
1823
1824        if (isReadUncommittedMode(lockMode)) {
1825            return LockType.NONE;
1826        } else if (lockMode == null || lockMode == LockMode.DEFAULT) {
1827            return rangeLock ? LockType.RANGE_READ: LockType.READ;
1828        } else if (lockMode == LockMode.RMW) {
1829            return rangeLock ? LockType.RANGE_WRITE: LockType.WRITE;
1830        } else if (lockMode == LockMode.READ_COMMITTED) {
1831            throw new IllegalArgumentException JavaDoc
1832                (lockMode.toString() + " not allowed with Cursor methods");
1833        } else {
1834            assert false : lockMode;
1835            return LockType.NONE;
1836        }
1837    }
1838
1839    /**
1840     * Returns whether the given lock mode will cause a read-uncommitted when
1841     * used with this cursor, taking into account the default cursor
1842     * configuration.
1843     */

1844    boolean isReadUncommittedMode(LockMode lockMode) {
1845        
1846        return (lockMode == LockMode.READ_UNCOMMITTED ||
1847                (readUncommittedDefault &&
1848                 (lockMode == null || lockMode == LockMode.DEFAULT)));
1849    }
1850    
1851    private boolean isSerializableIsolation(LockMode lockMode) {
1852
1853        return serializableIsolationDefault &&
1854               !isReadUncommittedMode(lockMode);
1855    }
1856
1857    protected void checkUpdatesAllowed(String JavaDoc operation)
1858        throws DatabaseException {
1859
1860        if (updateOperationsProhibited) {
1861            throw new DatabaseException
1862                ("A transaction was not supplied when opening this cursor: " +
1863                 operation);
1864        }
1865    }
1866
1867    /**
1868     * Note that this flavor of checkArgs doesn't require that the dbt data is
1869     * set.
1870     */

1871    private void checkArgsNoValRequired(DatabaseEntry key,
1872                                        DatabaseEntry data) {
1873        DatabaseUtil.checkForNullDbt(key, "key", false);
1874        DatabaseUtil.checkForNullDbt(data, "data", false);
1875    }
1876
1877    /**
1878     * Note that this flavor of checkArgs requires that the dbt data is set.
1879     */

1880    private void checkArgsValRequired(DatabaseEntry key,
1881                                      DatabaseEntry data) {
1882        DatabaseUtil.checkForNullDbt(key, "key", true);
1883        DatabaseUtil.checkForNullDbt(data, "data", true);
1884    }
1885
1886    /**
1887     * Check the environment and cursor state.
1888     */

1889    void checkState(boolean mustBeInitialized)
1890        throws DatabaseException {
1891
1892        checkEnv();
1893        cursorImpl.checkCursorState(mustBeInitialized);
1894    }
1895
1896    /**
1897     * @throws RunRecoveryException if the underlying environment is invalid.
1898     */

1899    void checkEnv()
1900        throws RunRecoveryException {
1901
1902        cursorImpl.checkEnv();
1903    }
1904
1905    /**
1906     * Send trace messages to the java.util.logger. Don't rely on the logger
1907     * alone to conditionalize whether we send this message, we don't even want
1908     * to construct the message if the level is not enabled.
1909     */

1910    void trace(Level JavaDoc level,
1911               String JavaDoc methodName,
1912               DatabaseEntry key,
1913               DatabaseEntry data,
1914               LockMode lockMode) {
1915        if (logger.isLoggable(level)) {
1916            StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1917            sb.append(methodName);
1918            traceCursorImpl(sb);
1919            if (key != null) {
1920                sb.append(" key=").append(key.dumpData());
1921            }
1922            if (data != null) {
1923                sb.append(" data=").append(data.dumpData());
1924            }
1925            if (lockMode != null) {
1926                sb.append(" lockMode=").append(lockMode);
1927            }
1928            logger.log(level, sb.toString());
1929        }
1930    }
1931    /**
1932     * Send trace messages to the java.util.logger. Don't rely on the logger
1933     * alone to conditionalize whether we send this message, we don't even want
1934     * to construct the message if the level is not enabled.
1935     */

1936    void trace(Level JavaDoc level, String JavaDoc methodName, LockMode lockMode) {
1937        if (logger.isLoggable(level)) {
1938            StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1939            sb.append(methodName);
1940            traceCursorImpl(sb);
1941            if (lockMode != null) {
1942                sb.append(" lockMode=").append(lockMode);
1943            }
1944            logger.log(level, sb.toString());
1945        }
1946    }
1947
1948    private void traceCursorImpl(StringBuffer JavaDoc sb) {
1949        sb.append(" locker=").append(cursorImpl.getLocker().getId());
1950        if (cursorImpl.getBIN() != null) {
1951            sb.append(" bin=").append(cursorImpl.getBIN().getNodeId());
1952        }
1953        sb.append(" idx=").append(cursorImpl.getIndex());
1954        
1955        if (cursorImpl.getDupBIN() != null) {
1956            sb.append(" Dbin=").append(cursorImpl.getDupBIN().getNodeId());
1957        }
1958        sb.append(" dupIdx=").append(cursorImpl.getDupIndex());
1959    }
1960}
1961
Popular Tags