001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2000,2008 Oracle. All rights reserved.
005: *
006: * $Id: DataCursor.java,v 1.60.2.3 2008/01/07 15:14:06 cwl Exp $
007: */
008:
009: package com.sleepycat.collections;
010:
011: import com.sleepycat.compat.DbCompat;
012: import com.sleepycat.je.Cursor;
013: import com.sleepycat.je.CursorConfig;
014: import com.sleepycat.je.DatabaseEntry;
015: import com.sleepycat.je.DatabaseException;
016: import com.sleepycat.je.JoinConfig;
017: import com.sleepycat.je.JoinCursor;
018: import com.sleepycat.je.LockMode;
019: import com.sleepycat.je.OperationStatus;
020: import com.sleepycat.util.keyrange.KeyRange;
021: import com.sleepycat.util.keyrange.RangeCursor;
022:
023: /**
024: * Represents a Berkeley DB cursor and adds support for indices, bindings and
025: * key ranges.
026: *
027: * <p>This class operates on a view and takes care of reading and updating
028: * indices, calling bindings, constraining access to a key range, etc.</p>
029: *
030: * @author Mark Hayes
031: */
032: final class DataCursor implements Cloneable {
033:
034: /** Repositioned exactly to the key/data pair given. */
035: static final int REPOS_EXACT = 0;
036: /** Repositioned on a record following the key/data pair given. */
037: static final int REPOS_NEXT = 1;
038: /** Repositioned failed, no records on or after the key/data pair given. */
039: static final int REPOS_EOF = 2;
040:
041: private RangeCursor cursor;
042: private JoinCursor joinCursor;
043: private DataView view;
044: private KeyRange range;
045: private boolean writeAllowed;
046: private boolean readUncommitted;
047: private DatabaseEntry keyThang;
048: private DatabaseEntry valueThang;
049: private DatabaseEntry primaryKeyThang;
050: private DatabaseEntry otherThang;
051: private DataCursor[] indexCursorsToClose;
052:
053: /**
054: * Creates a cursor for a given view.
055: */
056: DataCursor(DataView view, boolean writeAllowed)
057: throws DatabaseException {
058:
059: init(view, writeAllowed, null, null);
060: }
061:
062: /**
063: * Creates a cursor for a given view.
064: */
065: DataCursor(DataView view, boolean writeAllowed, CursorConfig config)
066: throws DatabaseException {
067:
068: init(view, writeAllowed, config, null);
069: }
070:
071: /**
072: * Creates a cursor for a given view and single key range.
073: * Used by unit tests.
074: */
075: DataCursor(DataView view, boolean writeAllowed, Object singleKey)
076: throws DatabaseException {
077:
078: init(view, writeAllowed, null, view.subRange(view.range,
079: singleKey));
080: }
081:
082: /**
083: * Creates a cursor for a given view and key range.
084: * Used by unit tests.
085: */
086: DataCursor(DataView view, boolean writeAllowed, Object beginKey,
087: boolean beginInclusive, Object endKey, boolean endInclusive)
088: throws DatabaseException {
089:
090: init(view, writeAllowed, null, view.subRange(view.range,
091: beginKey, beginInclusive, endKey, endInclusive));
092: }
093:
094: /**
095: * Creates a join cursor.
096: */
097: DataCursor(DataView view, DataCursor[] indexCursors,
098: JoinConfig joinConfig, boolean closeIndexCursors)
099: throws DatabaseException {
100:
101: if (view.isSecondary()) {
102: throw new IllegalArgumentException(
103: "The primary collection in a join must not be a secondary "
104: + "database");
105: }
106: Cursor[] cursors = new Cursor[indexCursors.length];
107: for (int i = 0; i < cursors.length; i += 1) {
108: cursors[i] = indexCursors[i].cursor.getCursor();
109: }
110: joinCursor = view.db.join(cursors, joinConfig);
111: init(view, false, null, null);
112: if (closeIndexCursors) {
113: indexCursorsToClose = indexCursors;
114: }
115: }
116:
117: /**
118: * Clones a cursor preserving the current position.
119: */
120: DataCursor cloneCursor() throws DatabaseException {
121:
122: checkNoJoinCursor();
123:
124: DataCursor o;
125: try {
126: o = (DataCursor) super .clone();
127: } catch (CloneNotSupportedException neverHappens) {
128: return null;
129: }
130:
131: o.initThangs();
132: KeyRange.copy(keyThang, o.keyThang);
133: KeyRange.copy(valueThang, o.valueThang);
134: if (primaryKeyThang != keyThang) {
135: KeyRange.copy(primaryKeyThang, o.primaryKeyThang);
136: }
137:
138: o.cursor = cursor.dup(true);
139: return o;
140: }
141:
142: /**
143: * Returns the internal range cursor.
144: */
145: RangeCursor getCursor() {
146: return cursor;
147: }
148:
149: /**
150: * Constructor helper.
151: */
152: private void init(DataView view, boolean writeAllowed,
153: CursorConfig config, KeyRange range)
154: throws DatabaseException {
155:
156: if (config == null) {
157: config = view.cursorConfig;
158: }
159: this .view = view;
160: this .writeAllowed = writeAllowed && view.writeAllowed;
161: this .range = (range != null) ? range : view.range;
162: readUncommitted = config.getReadUncommitted()
163: || view.currentTxn.isReadUncommitted();
164: initThangs();
165:
166: if (joinCursor == null) {
167: cursor = new MyRangeCursor(this .range, config, view,
168: this .writeAllowed);
169: }
170: }
171:
172: /**
173: * Constructor helper.
174: */
175: private void initThangs() throws DatabaseException {
176:
177: keyThang = new DatabaseEntry();
178: primaryKeyThang = view.isSecondary() ? (new DatabaseEntry())
179: : keyThang;
180: valueThang = new DatabaseEntry();
181: }
182:
183: /**
184: * Set entries from given byte arrays.
185: */
186: private void setThangs(byte[] keyBytes, byte[] priKeyBytes,
187: byte[] valueBytes) {
188:
189: keyThang.setData(KeyRange.copyBytes(keyBytes));
190:
191: if (keyThang != primaryKeyThang) {
192: primaryKeyThang.setData(KeyRange.copyBytes(priKeyBytes));
193: }
194:
195: valueThang.setData(KeyRange.copyBytes(valueBytes));
196: }
197:
198: /**
199: * Closes the associated cursor.
200: */
201: void close() throws DatabaseException {
202:
203: if (joinCursor != null) {
204: JoinCursor toClose = joinCursor;
205: joinCursor = null;
206: toClose.close();
207: }
208: if (cursor != null) {
209: Cursor toClose = cursor.getCursor();
210: cursor = null;
211: view.currentTxn.closeCursor(toClose);
212: }
213: if (indexCursorsToClose != null) {
214: DataCursor[] toClose = indexCursorsToClose;
215: indexCursorsToClose = null;
216: for (int i = 0; i < toClose.length; i += 1) {
217: toClose[i].close();
218: }
219: }
220: }
221:
222: /**
223: * Repositions to a given raw key/data pair, or just past it if that record
224: * has been deleted.
225: *
226: * @return REPOS_EXACT, REPOS_NEXT or REPOS_EOF.
227: */
228: int repositionRange(byte[] keyBytes, byte[] priKeyBytes,
229: byte[] valueBytes, boolean lockForWrite)
230: throws DatabaseException {
231:
232: LockMode lockMode = getLockMode(lockForWrite);
233: OperationStatus status = null;
234:
235: /* Use the given key/data byte arrays. */
236: setThangs(keyBytes, priKeyBytes, valueBytes);
237:
238: /* Position on or after the given key/data pair. */
239: if (view.dupsAllowed) {
240: status = cursor.getSearchBothRange(keyThang,
241: primaryKeyThang, valueThang, lockMode);
242: }
243: if (status != OperationStatus.SUCCESS) {
244: status = cursor.getSearchKeyRange(keyThang,
245: primaryKeyThang, valueThang, lockMode);
246: }
247:
248: /* Return the result of the operation. */
249: if (status == OperationStatus.SUCCESS) {
250: if (!KeyRange.equalBytes(keyBytes, 0, keyBytes.length,
251: keyThang.getData(), keyThang.getOffset(), keyThang
252: .getSize())) {
253: return REPOS_NEXT;
254: }
255: if (view.dupsAllowed) {
256: DatabaseEntry thang = view.isSecondary() ? primaryKeyThang
257: : valueThang;
258: byte[] bytes = view.isSecondary() ? priKeyBytes
259: : valueBytes;
260: if (!KeyRange.equalBytes(bytes, 0, bytes.length, thang
261: .getData(), thang.getOffset(), thang.getSize())) {
262: return REPOS_NEXT;
263: }
264: }
265: return REPOS_EXACT;
266: } else {
267: return REPOS_EOF;
268: }
269: }
270:
271: /**
272: * Repositions to a given raw key/data pair.
273: *
274: * @throws IllegalStateException when the database has unordered keys or
275: * unordered duplicates.
276: *
277: * @return whether the search succeeded.
278: */
279: boolean repositionExact(byte[] keyBytes, byte[] priKeyBytes,
280: byte[] valueBytes, boolean lockForWrite)
281: throws DatabaseException {
282:
283: LockMode lockMode = getLockMode(lockForWrite);
284: OperationStatus status = null;
285:
286: /* Use the given key/data byte arrays. */
287: setThangs(keyBytes, priKeyBytes, valueBytes);
288:
289: /* Position on the given key/data pair. */
290: if (view.recNumRenumber) {
291: /* getSearchBoth doesn't work with recno-renumber databases. */
292: status = cursor.getSearchKey(keyThang, primaryKeyThang,
293: valueThang, lockMode);
294: } else {
295: status = cursor.getSearchBoth(keyThang, primaryKeyThang,
296: valueThang, lockMode);
297: }
298:
299: return (status == OperationStatus.SUCCESS);
300: }
301:
302: /**
303: * Returns the view for this cursor.
304: */
305: DataView getView() {
306:
307: return view;
308: }
309:
310: /**
311: * Returns the range for this cursor.
312: */
313: KeyRange getRange() {
314:
315: return range;
316: }
317:
318: /**
319: * Returns whether write is allowed for this cursor, as specified to the
320: * constructor.
321: */
322: boolean isWriteAllowed() {
323:
324: return writeAllowed;
325: }
326:
327: /**
328: * Returns the key object for the last record read.
329: */
330: Object getCurrentKey() throws DatabaseException {
331:
332: return view.makeKey(keyThang, primaryKeyThang);
333: }
334:
335: /**
336: * Returns the value object for the last record read.
337: */
338: Object getCurrentValue() throws DatabaseException {
339:
340: return view.makeValue(primaryKeyThang, valueThang);
341: }
342:
343: /**
344: * Returns the internal key entry.
345: */
346: DatabaseEntry getKeyThang() {
347: return keyThang;
348: }
349:
350: /**
351: * Returns the internal primary key entry, which is the same object as the
352: * key entry if the cursor is not for a secondary database.
353: */
354: DatabaseEntry getPrimaryKeyThang() {
355: return primaryKeyThang;
356: }
357:
358: /**
359: * Returns the internal value entry.
360: */
361: DatabaseEntry getValueThang() {
362: return valueThang;
363: }
364:
365: /**
366: * Returns whether record number access is allowed.
367: */
368: boolean hasRecNumAccess() {
369:
370: return view.recNumAccess;
371: }
372:
373: /**
374: * Returns the record number for the last record read.
375: */
376: int getCurrentRecordNumber() throws DatabaseException {
377:
378: if (view.btreeRecNumDb) {
379: /* BTREE-RECNO access. */
380: if (otherThang == null) {
381: otherThang = new DatabaseEntry();
382: }
383: DbCompat.getCurrentRecordNumber(cursor.getCursor(),
384: otherThang, getLockMode(false));
385: return DbCompat.getRecordNumber(otherThang);
386: } else {
387: /* QUEUE or RECNO database. */
388: return DbCompat.getRecordNumber(keyThang);
389: }
390: }
391:
392: /**
393: * Binding version of Cursor.getCurrent(), no join cursor allowed.
394: */
395: OperationStatus getCurrent(boolean lockForWrite)
396: throws DatabaseException {
397:
398: checkNoJoinCursor();
399: return cursor.getCurrent(keyThang, primaryKeyThang, valueThang,
400: getLockMode(lockForWrite));
401: }
402:
403: /**
404: * Binding version of Cursor.getFirst(), join cursor is allowed.
405: */
406: OperationStatus getFirst(boolean lockForWrite)
407: throws DatabaseException {
408:
409: LockMode lockMode = getLockMode(lockForWrite);
410: if (joinCursor != null) {
411: return joinCursor.getNext(keyThang, valueThang, lockMode);
412: } else {
413: return cursor.getFirst(keyThang, primaryKeyThang,
414: valueThang, lockMode);
415: }
416: }
417:
418: /**
419: * Binding version of Cursor.getNext(), join cursor is allowed.
420: */
421: OperationStatus getNext(boolean lockForWrite)
422: throws DatabaseException {
423:
424: LockMode lockMode = getLockMode(lockForWrite);
425: if (joinCursor != null) {
426: return joinCursor.getNext(keyThang, valueThang, lockMode);
427: } else {
428: return cursor.getNext(keyThang, primaryKeyThang,
429: valueThang, lockMode);
430: }
431: }
432:
433: /**
434: * Binding version of Cursor.getNext(), join cursor is allowed.
435: */
436: OperationStatus getNextNoDup(boolean lockForWrite)
437: throws DatabaseException {
438:
439: LockMode lockMode = getLockMode(lockForWrite);
440: if (joinCursor != null) {
441: return joinCursor.getNext(keyThang, valueThang, lockMode);
442: } else if (view.dupsView) {
443: return cursor.getNext(keyThang, primaryKeyThang,
444: valueThang, lockMode);
445: } else {
446: return cursor.getNextNoDup(keyThang, primaryKeyThang,
447: valueThang, lockMode);
448: }
449: }
450:
451: /**
452: * Binding version of Cursor.getNextDup(), no join cursor allowed.
453: */
454: OperationStatus getNextDup(boolean lockForWrite)
455: throws DatabaseException {
456:
457: checkNoJoinCursor();
458: if (view.dupsView) {
459: return null;
460: } else {
461: return cursor.getNextDup(keyThang, primaryKeyThang,
462: valueThang, getLockMode(lockForWrite));
463: }
464: }
465:
466: /**
467: * Binding version of Cursor.getLast(), no join cursor allowed.
468: */
469: OperationStatus getLast(boolean lockForWrite)
470: throws DatabaseException {
471:
472: checkNoJoinCursor();
473: return cursor.getLast(keyThang, primaryKeyThang, valueThang,
474: getLockMode(lockForWrite));
475: }
476:
477: /**
478: * Binding version of Cursor.getPrev(), no join cursor allowed.
479: */
480: OperationStatus getPrev(boolean lockForWrite)
481: throws DatabaseException {
482:
483: checkNoJoinCursor();
484: return cursor.getPrev(keyThang, primaryKeyThang, valueThang,
485: getLockMode(lockForWrite));
486: }
487:
488: /**
489: * Binding version of Cursor.getPrevNoDup(), no join cursor allowed.
490: */
491: OperationStatus getPrevNoDup(boolean lockForWrite)
492: throws DatabaseException {
493:
494: checkNoJoinCursor();
495: LockMode lockMode = getLockMode(lockForWrite);
496: if (view.dupsView) {
497: return null;
498: } else if (view.dupsView) {
499: return cursor.getPrev(keyThang, primaryKeyThang,
500: valueThang, lockMode);
501: } else {
502: return cursor.getPrevNoDup(keyThang, primaryKeyThang,
503: valueThang, lockMode);
504: }
505: }
506:
507: /**
508: * Binding version of Cursor.getPrevDup(), no join cursor allowed.
509: */
510: OperationStatus getPrevDup(boolean lockForWrite)
511: throws DatabaseException {
512:
513: checkNoJoinCursor();
514: if (view.dupsView) {
515: return null;
516: } else {
517: return cursor.getPrevDup(keyThang, primaryKeyThang,
518: valueThang, getLockMode(lockForWrite));
519: }
520: }
521:
522: /**
523: * Binding version of Cursor.getSearchKey(), no join cursor allowed.
524: * Searches by record number in a BTREE-RECNO db with RECNO access.
525: */
526: OperationStatus getSearchKey(Object key, Object value,
527: boolean lockForWrite) throws DatabaseException {
528:
529: checkNoJoinCursor();
530: if (view.dupsView) {
531: if (view
532: .useKey(key, value, primaryKeyThang, view.dupsRange)) {
533: KeyRange.copy(view.dupsKey, keyThang);
534: return cursor.getSearchBoth(keyThang, primaryKeyThang,
535: valueThang, getLockMode(lockForWrite));
536: }
537: } else {
538: if (view.useKey(key, value, keyThang, range)) {
539: return doGetSearchKey(lockForWrite);
540: }
541: }
542: return OperationStatus.NOTFOUND;
543: }
544:
545: /**
546: * Pass-thru version of Cursor.getSearchKey().
547: * Searches by record number in a BTREE-RECNO db with RECNO access.
548: */
549: private OperationStatus doGetSearchKey(boolean lockForWrite)
550: throws DatabaseException {
551:
552: LockMode lockMode = getLockMode(lockForWrite);
553: if (view.btreeRecNumAccess) {
554: return cursor.getSearchRecordNumber(keyThang,
555: primaryKeyThang, valueThang, lockMode);
556: } else {
557: return cursor.getSearchKey(keyThang, primaryKeyThang,
558: valueThang, lockMode);
559: }
560: }
561:
562: /**
563: * Binding version of Cursor.getSearchKeyRange(), no join cursor allowed.
564: */
565: OperationStatus getSearchKeyRange(Object key, Object value,
566: boolean lockForWrite) throws DatabaseException {
567:
568: checkNoJoinCursor();
569: LockMode lockMode = getLockMode(lockForWrite);
570: if (view.dupsView) {
571: if (view
572: .useKey(key, value, primaryKeyThang, view.dupsRange)) {
573: KeyRange.copy(view.dupsKey, keyThang);
574: return cursor.getSearchBothRange(keyThang,
575: primaryKeyThang, valueThang, lockMode);
576: }
577: } else {
578: if (view.useKey(key, value, keyThang, range)) {
579: return cursor.getSearchKeyRange(keyThang,
580: primaryKeyThang, valueThang, lockMode);
581: }
582: }
583: return OperationStatus.NOTFOUND;
584: }
585:
586: /**
587: * Find the given key and value using getSearchBoth if possible or a
588: * sequential scan otherwise, no join cursor allowed.
589: */
590: OperationStatus findBoth(Object key, Object value,
591: boolean lockForWrite) throws DatabaseException {
592:
593: checkNoJoinCursor();
594: LockMode lockMode = getLockMode(lockForWrite);
595: view.useValue(value, valueThang, null);
596: if (view.dupsView) {
597: if (view
598: .useKey(key, value, primaryKeyThang, view.dupsRange)) {
599: KeyRange.copy(view.dupsKey, keyThang);
600: if (otherThang == null) {
601: otherThang = new DatabaseEntry();
602: }
603: OperationStatus status = cursor.getSearchBoth(keyThang,
604: primaryKeyThang, otherThang, lockMode);
605: if (status == OperationStatus.SUCCESS
606: && KeyRange.equalBytes(otherThang, valueThang)) {
607: return status;
608: }
609: }
610: } else if (view.useKey(key, value, keyThang, range)) {
611: if (view.isSecondary()) {
612: if (otherThang == null) {
613: otherThang = new DatabaseEntry();
614: }
615: OperationStatus status = cursor.getSearchKey(keyThang,
616: primaryKeyThang, otherThang, lockMode);
617: while (status == OperationStatus.SUCCESS) {
618: if (KeyRange.equalBytes(otherThang, valueThang)) {
619: return status;
620: }
621: status = cursor.getNextDup(keyThang,
622: primaryKeyThang, otherThang, lockMode);
623: }
624: /* if status != SUCCESS set range cursor to invalid? */
625: } else {
626: return cursor.getSearchBoth(keyThang, null, valueThang,
627: lockMode);
628: }
629: }
630: return OperationStatus.NOTFOUND;
631: }
632:
633: /**
634: * Find the given value using getSearchBoth if possible or a sequential
635: * scan otherwise, no join cursor allowed.
636: */
637: OperationStatus findValue(Object value, boolean findFirst)
638: throws DatabaseException {
639:
640: checkNoJoinCursor();
641:
642: if (view.entityBinding != null && !view.isSecondary()
643: && (findFirst || !view.dupsAllowed)) {
644: return findBoth(null, value, false);
645: } else {
646: if (otherThang == null) {
647: otherThang = new DatabaseEntry();
648: }
649: view.useValue(value, otherThang, null);
650: OperationStatus status = findFirst ? getFirst(false)
651: : getLast(false);
652: while (status == OperationStatus.SUCCESS) {
653: if (KeyRange.equalBytes(valueThang, otherThang)) {
654: break;
655: }
656: status = findFirst ? getNext(false) : getPrev(false);
657: }
658: return status;
659: }
660: }
661:
662: /**
663: * Calls Cursor.count(), no join cursor allowed.
664: */
665: int count() throws DatabaseException {
666:
667: checkNoJoinCursor();
668: if (view.dupsView) {
669: return 1;
670: } else {
671: return cursor.count();
672: }
673: }
674:
675: /**
676: * Binding version of Cursor.putCurrent().
677: */
678: OperationStatus putCurrent(Object value) throws DatabaseException {
679:
680: checkWriteAllowed(false);
681: view.useValue(value, valueThang, keyThang);
682:
683: /*
684: * Workaround for a DB core problem: With HASH type a put() with
685: * different data is allowed.
686: */
687: boolean hashWorkaround = (view.dupsOrdered && !view.ordered);
688: if (hashWorkaround) {
689: if (otherThang == null) {
690: otherThang = new DatabaseEntry();
691: }
692: cursor.getCurrent(keyThang, primaryKeyThang, otherThang,
693: LockMode.DEFAULT);
694: if (KeyRange.equalBytes(valueThang, otherThang)) {
695: return OperationStatus.SUCCESS;
696: } else {
697: throw new IllegalArgumentException(
698: "Current data differs from put data with sorted duplicates");
699: }
700: }
701:
702: return cursor.putCurrent(valueThang);
703: }
704:
705: /**
706: * Binding version of Cursor.putAfter().
707: */
708: OperationStatus putAfter(Object value) throws DatabaseException {
709:
710: checkWriteAllowed(false);
711: view.useValue(value, valueThang, null); /* why no key check? */
712: return cursor.putAfter(keyThang, valueThang);
713: }
714:
715: /**
716: * Binding version of Cursor.putBefore().
717: */
718: OperationStatus putBefore(Object value) throws DatabaseException {
719:
720: checkWriteAllowed(false);
721: view.useValue(value, valueThang, keyThang);
722: return cursor.putBefore(keyThang, valueThang);
723: }
724:
725: /**
726: * Binding version of Cursor.put(), optionally returning the old value and
727: * optionally using the current key instead of the key parameter.
728: */
729: OperationStatus put(Object key, Object value, Object[] oldValue,
730: boolean useCurrentKey) throws DatabaseException {
731:
732: initForPut(key, value, oldValue, useCurrentKey);
733: return cursor.put(keyThang, valueThang);
734: }
735:
736: /**
737: * Binding version of Cursor.putNoOverwrite(), optionally using the current
738: * key instead of the key parameter.
739: */
740: OperationStatus putNoOverwrite(Object key, Object value,
741: boolean useCurrentKey) throws DatabaseException {
742:
743: initForPut(key, value, null, useCurrentKey);
744: return cursor.putNoOverwrite(keyThang, valueThang);
745: }
746:
747: /**
748: * Binding version of Cursor.putNoDupData(), optionally returning the old
749: * value and optionally using the current key instead of the key parameter.
750: */
751: OperationStatus putNoDupData(Object key, Object value,
752: Object[] oldValue, boolean useCurrentKey)
753: throws DatabaseException {
754:
755: initForPut(key, value, oldValue, useCurrentKey);
756: if (view.dupsOrdered) {
757: return cursor.putNoDupData(keyThang, valueThang);
758: } else {
759: if (view.dupsAllowed) {
760: /* Unordered duplicates. */
761: OperationStatus status = cursor
762: .getSearchBoth(keyThang, primaryKeyThang,
763: valueThang, getLockMode(false));
764: if (status == OperationStatus.SUCCESS) {
765: return OperationStatus.KEYEXIST;
766: } else {
767: return cursor.put(keyThang, valueThang);
768: }
769: } else {
770: /* No duplicates. */
771: return cursor.putNoOverwrite(keyThang, valueThang);
772: }
773: }
774: }
775:
776: /**
777: * Do setup for a put() operation.
778: */
779: private void initForPut(Object key, Object value,
780: Object[] oldValue, boolean useCurrentKey)
781: throws DatabaseException {
782:
783: checkWriteAllowed(false);
784: if (!useCurrentKey && !view.useKey(key, value, keyThang, range)) {
785: throw new IllegalArgumentException("key out of range");
786: }
787: if (oldValue != null) {
788: oldValue[0] = null;
789: if (!view.dupsAllowed) {
790: OperationStatus status = doGetSearchKey(true);
791: if (status == OperationStatus.SUCCESS) {
792: oldValue[0] = getCurrentValue();
793: }
794: }
795: }
796: view.useValue(value, valueThang, keyThang);
797: }
798:
799: /**
800: * Sets the key entry to the begin key of a single key range, so the next
801: * time a putXxx() method is called that key will be used.
802: */
803: void useRangeKey() {
804: if (!range.isSingleKey()) {
805: throw new IllegalStateException();
806: }
807: KeyRange.copy(range.getSingleKey(), keyThang);
808: }
809:
810: /**
811: * Perform an arbitrary database 'delete' operation.
812: */
813: OperationStatus delete() throws DatabaseException {
814:
815: checkWriteAllowed(true);
816: return cursor.delete();
817: }
818:
819: /**
820: * Returns the lock mode to use for a getXxx() operation.
821: */
822: LockMode getLockMode(boolean lockForWrite) {
823:
824: /* Read-uncommmitted takes precedence over write-locking. */
825:
826: if (readUncommitted) {
827: return LockMode.READ_UNCOMMITTED;
828: } else if (lockForWrite) {
829: return view.currentTxn.getWriteLockMode();
830: } else {
831: return LockMode.DEFAULT;
832: }
833: }
834:
835: /**
836: * Throws an exception if a join cursor is in use.
837: */
838: private void checkNoJoinCursor() {
839:
840: if (joinCursor != null) {
841: throw new UnsupportedOperationException(
842: "Not allowed with a join cursor");
843: }
844: }
845:
846: /**
847: * Throws an exception if write is not allowed or if a join cursor is in
848: * use.
849: */
850: private void checkWriteAllowed(boolean allowSecondary) {
851:
852: checkNoJoinCursor();
853:
854: if (!writeAllowed || (!allowSecondary && view.isSecondary())) {
855: throw new UnsupportedOperationException(
856: "Writing is not allowed");
857: }
858: }
859: }
|