00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 package com.sleepycat.collections;
00011
00012 import com.sleepycat.compat.DbCompat;
00013 import com.sleepycat.db.Cursor;
00014 import com.sleepycat.db.DatabaseEntry;
00015 import com.sleepycat.db.DatabaseException;
00016 import com.sleepycat.db.JoinConfig;
00017 import com.sleepycat.db.JoinCursor;
00018 import com.sleepycat.db.LockMode;
00019 import com.sleepycat.db.OperationStatus;
00020
00030 final class DataCursor implements Cloneable {
00031
00032 private RangeCursor cursor;
00033 private JoinCursor joinCursor;
00034 private DataView view;
00035 private KeyRange range;
00036 private boolean writeAllowed;
00037 private boolean readUncommitted;
00038 private DatabaseEntry keyThang;
00039 private DatabaseEntry valueThang;
00040 private DatabaseEntry primaryKeyThang;
00041 private DatabaseEntry otherThang;
00042 private DataCursor[] indexCursorsToClose;
00043
00047 DataCursor(DataView view, boolean writeAllowed)
00048 throws DatabaseException {
00049
00050 init(view, writeAllowed, null);
00051 }
00052
00056 DataCursor(DataView view, boolean writeAllowed, Object singleKey)
00057 throws DatabaseException {
00058
00059 init(view, writeAllowed, view.subRange(singleKey));
00060 }
00061
00065 DataCursor(DataView view, boolean writeAllowed,
00066 Object beginKey, boolean beginInclusive,
00067 Object endKey, boolean endInclusive)
00068 throws DatabaseException {
00069
00070 init(view, writeAllowed,
00071 view.subRange(beginKey, beginInclusive, endKey, endInclusive));
00072 }
00073
00077 DataCursor(DataView view, DataCursor[] indexCursors,
00078 JoinConfig joinConfig, boolean closeIndexCursors)
00079 throws DatabaseException {
00080
00081 if (view.isSecondary()) {
00082 throw new IllegalArgumentException(
00083 "The primary collection in a join must not be a secondary " +
00084 "database");
00085 }
00086 Cursor[] cursors = new Cursor[indexCursors.length];
00087 for (int i = 0; i < cursors.length; i += 1) {
00088 cursors[i] = indexCursors[i].cursor.getCursor();
00089 }
00090 joinCursor = view.db.join(cursors, joinConfig);
00091 init(view, false, null);
00092 if (closeIndexCursors) {
00093 indexCursorsToClose = indexCursors;
00094 }
00095 }
00096
00100 DataCursor cloneCursor()
00101 throws DatabaseException {
00102
00103 checkNoJoinCursor();
00104
00105 DataCursor o;
00106 try {
00107 o = (DataCursor) super.clone();
00108 } catch (CloneNotSupportedException neverHappens) {
00109 return null;
00110 }
00111
00112 o.initThangs();
00113 KeyRange.copy(keyThang, o.keyThang);
00114 KeyRange.copy(valueThang, o.valueThang);
00115 if (primaryKeyThang != keyThang) {
00116 KeyRange.copy(primaryKeyThang, o.primaryKeyThang);
00117 }
00118
00119 o.cursor = cursor.dup(true);
00120 return o;
00121 }
00122
00126 RangeCursor getCursor() {
00127 return cursor;
00128 }
00129
00133 private void init(DataView view, boolean writeAllowed, KeyRange range)
00134 throws DatabaseException {
00135
00136 this.view = view;
00137 this.writeAllowed = writeAllowed && view.writeAllowed;
00138 this.range = (range != null) ? range : view.range;
00139 readUncommitted = view.cursorConfig.getReadUncommitted() ||
00140 view.currentTxn.isReadUncommitted();
00141 initThangs();
00142
00143 if (joinCursor == null) {
00144 cursor = new RangeCursor(view, this.range, this.writeAllowed);
00145 }
00146 }
00147
00151 private void initThangs()
00152 throws DatabaseException {
00153
00154 keyThang = new DatabaseEntry();
00155 primaryKeyThang = view.isSecondary() ? (new DatabaseEntry())
00156 : keyThang;
00157 valueThang = new DatabaseEntry();
00158 }
00159
00163 void close()
00164 throws DatabaseException {
00165
00166 if (joinCursor != null) {
00167 JoinCursor toClose = joinCursor;
00168 joinCursor = null;
00169 toClose.close();
00170 }
00171 if (cursor != null) {
00172 Cursor toClose = cursor.getCursor();
00173 cursor = null;
00174 view.currentTxn.closeCursor(toClose );
00175 }
00176 if (indexCursorsToClose != null) {
00177 DataCursor[] toClose = indexCursorsToClose;
00178 indexCursorsToClose = null;
00179 for (int i = 0; i < toClose.length; i += 1) {
00180 toClose[i].close();
00181 }
00182 }
00183 }
00184
00188 DataView getView() {
00189
00190 return view;
00191 }
00192
00196 KeyRange getRange() {
00197
00198 return range;
00199 }
00200
00205 boolean isWriteAllowed() {
00206
00207 return writeAllowed;
00208 }
00209
00213 Object getCurrentKey()
00214 throws DatabaseException {
00215
00216 if (view.keyBinding == null) {
00217 throw new UnsupportedOperationException();
00218 }
00219 return view.makeKey(keyThang);
00220 }
00221
00225 Object getCurrentValue()
00226 throws DatabaseException {
00227
00228 return view.makeValue(primaryKeyThang, valueThang);
00229 }
00230
00234 boolean hasRecNumAccess() {
00235
00236 return view.recNumAccess;
00237 }
00238
00242 int getCurrentRecordNumber()
00243 throws DatabaseException {
00244
00245 if (view.btreeRecNumDb) {
00246
00247 if (otherThang == null) {
00248 otherThang = new DatabaseEntry();
00249 }
00250 DbCompat.getCurrentRecordNumber(cursor.getCursor(), otherThang,
00251 getLockMode(false));
00252 return DbCompat.getRecordNumber(otherThang);
00253 } else {
00254
00255 return DbCompat.getRecordNumber(keyThang);
00256 }
00257 }
00258
00262 OperationStatus getCurrent(boolean lockForWrite)
00263 throws DatabaseException {
00264
00265 checkNoJoinCursor();
00266 return cursor.getCurrent(keyThang, primaryKeyThang, valueThang,
00267 getLockMode(lockForWrite));
00268 }
00269
00273 OperationStatus getFirst(boolean lockForWrite)
00274 throws DatabaseException {
00275
00276 LockMode lockMode = getLockMode(lockForWrite);
00277 if (joinCursor != null) {
00278 return joinCursor.getNext(keyThang, valueThang, lockMode);
00279 } else {
00280 return cursor.getFirst(keyThang, primaryKeyThang, valueThang,
00281 lockMode);
00282 }
00283 }
00284
00288 OperationStatus getNext(boolean lockForWrite)
00289 throws DatabaseException {
00290
00291 LockMode lockMode = getLockMode(lockForWrite);
00292 if (joinCursor != null) {
00293 return joinCursor.getNext(keyThang, valueThang, lockMode);
00294 } else {
00295 return cursor.getNext(keyThang, primaryKeyThang, valueThang,
00296 lockMode);
00297 }
00298 }
00299
00303 OperationStatus getNextNoDup(boolean lockForWrite)
00304 throws DatabaseException {
00305
00306 LockMode lockMode = getLockMode(lockForWrite);
00307 if (joinCursor != null) {
00308 return joinCursor.getNext(keyThang, valueThang, lockMode);
00309 } else {
00310 return cursor.getNextNoDup(keyThang, primaryKeyThang, valueThang,
00311 lockMode);
00312 }
00313 }
00314
00318 OperationStatus getNextDup(boolean lockForWrite)
00319 throws DatabaseException {
00320
00321 checkNoJoinCursor();
00322 return cursor.getNextDup(keyThang, primaryKeyThang, valueThang,
00323 getLockMode(lockForWrite));
00324 }
00325
00329 OperationStatus getLast(boolean lockForWrite)
00330 throws DatabaseException {
00331
00332 checkNoJoinCursor();
00333 return cursor.getLast(keyThang, primaryKeyThang, valueThang,
00334 getLockMode(lockForWrite));
00335 }
00336
00340 OperationStatus getPrev(boolean lockForWrite)
00341 throws DatabaseException {
00342
00343 checkNoJoinCursor();
00344 return cursor.getPrev(keyThang, primaryKeyThang, valueThang,
00345 getLockMode(lockForWrite));
00346 }
00347
00351 OperationStatus getPrevNoDup(boolean lockForWrite)
00352 throws DatabaseException {
00353
00354 checkNoJoinCursor();
00355 return cursor.getPrevNoDup(keyThang, primaryKeyThang, valueThang,
00356 getLockMode(lockForWrite));
00357 }
00358
00362 OperationStatus getPrevDup(boolean lockForWrite)
00363 throws DatabaseException {
00364
00365 checkNoJoinCursor();
00366 return cursor.getPrevDup(keyThang, primaryKeyThang, valueThang,
00367 getLockMode(lockForWrite));
00368 }
00369
00374 OperationStatus getSearchKey(Object key, Object value,
00375 boolean lockForWrite)
00376 throws DatabaseException {
00377
00378 checkNoJoinCursor();
00379 if (view.useKey(key, value, keyThang, range)) {
00380 return doGetSearchKey(lockForWrite);
00381 } else {
00382 return OperationStatus.NOTFOUND;
00383 }
00384 }
00385
00390 private OperationStatus doGetSearchKey(boolean lockForWrite)
00391 throws DatabaseException {
00392
00393 LockMode lockMode = getLockMode(lockForWrite);
00394 if (view.btreeRecNumAccess) {
00395 return cursor.getSearchRecordNumber(keyThang, primaryKeyThang,
00396 valueThang, lockMode);
00397 } else {
00398 return cursor.getSearchKey(keyThang, primaryKeyThang,
00399 valueThang, lockMode);
00400 }
00401 }
00402
00406 OperationStatus getSearchKeyRange(Object key, Object value,
00407 boolean lockForWrite)
00408 throws DatabaseException {
00409
00410 checkNoJoinCursor();
00411 if (view.useKey(key, value, keyThang, range)) {
00412 return cursor.getSearchKeyRange(keyThang, primaryKeyThang,
00413 valueThang,
00414 getLockMode(lockForWrite));
00415 } else {
00416 return OperationStatus.NOTFOUND;
00417 }
00418 }
00419
00425 OperationStatus getSearchBoth(Object key, Object value,
00426 boolean lockForWrite)
00427 throws DatabaseException {
00428
00429 checkNoJoinCursor();
00430 LockMode lockMode = getLockMode(lockForWrite);
00431 view.useValue(value, valueThang, null);
00432 if (view.useKey(key, value, keyThang, range)) {
00433 if (view.isSecondary()) {
00434 if (otherThang == null) {
00435 otherThang = new DatabaseEntry();
00436 }
00437 OperationStatus status = cursor.getSearchKey(keyThang,
00438 primaryKeyThang,
00439 otherThang,
00440 lockMode);
00441 while (status == OperationStatus.SUCCESS) {
00442 if (KeyRange.equalBytes(otherThang, valueThang)) {
00443 break;
00444 }
00445 status = cursor.getNextDup(keyThang, primaryKeyThang,
00446 otherThang, lockMode);
00447 }
00448
00449 return status;
00450 } else {
00451 return cursor.getSearchBoth(keyThang, null, valueThang,
00452 lockMode);
00453 }
00454 } else {
00455 return OperationStatus.NOTFOUND;
00456 }
00457 }
00458
00463 OperationStatus find(Object value, boolean findFirst)
00464 throws DatabaseException {
00465
00466 checkNoJoinCursor();
00467
00468 if (view.entityBinding != null && !view.isSecondary() &&
00469 (findFirst || !view.dupsAllowed)) {
00470 return getSearchBoth(null, value, false);
00471 } else {
00472 if (otherThang == null) {
00473 otherThang = new DatabaseEntry();
00474 }
00475 view.useValue(value, otherThang, null);
00476 OperationStatus status = findFirst ? getFirst(false)
00477 : getLast(false);
00478 while (status == OperationStatus.SUCCESS) {
00479 if (KeyRange.equalBytes(valueThang, otherThang)) {
00480 break;
00481 }
00482 status = findFirst ? getNext(false) : getPrev(false);
00483 }
00484 return status;
00485 }
00486 }
00487
00491 int count()
00492 throws DatabaseException {
00493
00494 checkNoJoinCursor();
00495 return cursor.count();
00496 }
00497
00501 OperationStatus putCurrent(Object value)
00502 throws DatabaseException {
00503
00504 checkWriteAllowed(false);
00505 view.useValue(value, valueThang, keyThang);
00506
00507
00508
00509
00510
00511 boolean hashWorkaround = (view.dupsOrdered && !view.ordered);
00512 if (hashWorkaround) {
00513 if (otherThang == null) {
00514 otherThang = new DatabaseEntry();
00515 }
00516 cursor.getCurrent(keyThang, primaryKeyThang, otherThang,
00517 LockMode.DEFAULT);
00518 if (KeyRange.equalBytes(valueThang, otherThang)) {
00519 return OperationStatus.SUCCESS;
00520 } else {
00521 throw new IllegalArgumentException(
00522 "Current data differs from put data with sorted duplicates");
00523 }
00524 }
00525
00526 return cursor.putCurrent(valueThang);
00527 }
00528
00532 OperationStatus putAfter(Object value)
00533 throws DatabaseException {
00534
00535 checkWriteAllowed(false);
00536 view.useValue(value, valueThang, null);
00537 return cursor.putAfter(new DatabaseEntry(), valueThang);
00538 }
00539
00543 OperationStatus putBefore(Object value)
00544 throws DatabaseException {
00545
00546 checkWriteAllowed(false);
00547 view.useValue(value, valueThang, keyThang);
00548 return cursor.putBefore(new DatabaseEntry(), valueThang);
00549 }
00550
00555 OperationStatus put(Object key, Object value, Object[] oldValue,
00556 boolean useCurrentKey)
00557 throws DatabaseException {
00558
00559 initForPut(key, value, oldValue, useCurrentKey);
00560 return cursor.put(keyThang, valueThang);
00561 }
00562
00567 OperationStatus putNoOverwrite(Object key, Object value,
00568 boolean useCurrentKey)
00569 throws DatabaseException {
00570
00571 initForPut(key, value, null, useCurrentKey);
00572 return cursor.putNoOverwrite(keyThang, valueThang);
00573 }
00574
00579 OperationStatus putNoDupData(Object key, Object value, Object[] oldValue,
00580 boolean useCurrentKey)
00581 throws DatabaseException {
00582
00583 initForPut(key, value, oldValue, useCurrentKey);
00584 if (view.dupsOrdered) {
00585 return cursor.putNoDupData(keyThang, valueThang);
00586 } else {
00587 if (view.dupsAllowed) {
00588
00589 OperationStatus status =
00590 cursor.getSearchBoth(keyThang, primaryKeyThang,
00591 valueThang,
00592 getLockMode(false));
00593 if (status == OperationStatus.SUCCESS) {
00594 return OperationStatus.KEYEXIST;
00595 } else {
00596 return cursor.put(keyThang, valueThang);
00597 }
00598 } else {
00599
00600 return cursor.putNoOverwrite(keyThang, valueThang);
00601 }
00602 }
00603 }
00604
00608 private void initForPut(Object key, Object value, Object[] oldValue,
00609 boolean useCurrentKey)
00610 throws DatabaseException {
00611
00612 checkWriteAllowed(false);
00613 if (!useCurrentKey && !view.useKey(key, value, keyThang, range)) {
00614 throw new IllegalArgumentException("key out of range");
00615 }
00616 if (oldValue != null) {
00617 oldValue[0] = null;
00618 if (!view.dupsAllowed) {
00619 OperationStatus status = doGetSearchKey(true);
00620 if (status == OperationStatus.SUCCESS) {
00621 oldValue[0] = getCurrentValue();
00622 }
00623 }
00624 }
00625 view.useValue(value, valueThang, keyThang);
00626 }
00627
00632 void useRangeKey() {
00633 if (!range.singleKey) {
00634 throw new IllegalStateException();
00635 }
00636 KeyRange.copy(range.beginKey, keyThang);
00637 }
00638
00642 OperationStatus delete()
00643 throws DatabaseException {
00644
00645 checkWriteAllowed(true);
00646 return cursor.delete();
00647 }
00648
00652 LockMode getLockMode(boolean lockForWrite) {
00653
00654
00655
00656 if (lockForWrite && !readUncommitted) {
00657 return view.currentTxn.getWriteLockMode();
00658 } else {
00659 return LockMode.DEFAULT;
00660 }
00661 }
00662
00666 private void checkNoJoinCursor() {
00667
00668 if (joinCursor != null) {
00669 throw new UnsupportedOperationException
00670 ("Not allowed with a join cursor");
00671 }
00672 }
00673
00678 private void checkWriteAllowed(boolean allowSecondary) {
00679
00680 checkNoJoinCursor();
00681
00682 if (!writeAllowed || (!allowSecondary && view.isSecondary())) {
00683 throw new UnsupportedOperationException
00684 ("Writing is not allowed");
00685 }
00686 }
00687 }