00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 package com.sleepycat.collections;
00011
00012 import com.sleepycat.bind.EntityBinding;
00013 import com.sleepycat.bind.EntryBinding;
00014 import com.sleepycat.compat.DbCompat;
00015 import com.sleepycat.db.CursorConfig;
00016 import com.sleepycat.db.Database;
00017 import com.sleepycat.db.DatabaseConfig;
00018 import com.sleepycat.db.DatabaseEntry;
00019 import com.sleepycat.db.DatabaseException;
00020 import com.sleepycat.db.Environment;
00021 import com.sleepycat.db.JoinConfig;
00022 import com.sleepycat.db.OperationStatus;
00023 import com.sleepycat.db.SecondaryConfig;
00024 import com.sleepycat.db.SecondaryDatabase;
00025 import com.sleepycat.db.SecondaryKeyCreator;
00026 import com.sleepycat.db.Transaction;
00027 import com.sleepycat.util.RuntimeExceptionWrapper;
00028
00038 final class DataView implements Cloneable {
00039
00040 Database db;
00041 SecondaryDatabase secDb;
00042 CurrentTransaction currentTxn;
00043 KeyRange range;
00044 EntryBinding keyBinding;
00045 EntryBinding valueBinding;
00046 EntityBinding entityBinding;
00047 PrimaryKeyAssigner keyAssigner;
00048 SecondaryKeyCreator secKeyCreator;
00049 CursorConfig cursorConfig;
00050 boolean writeAllowed;
00051 boolean ordered;
00052 boolean recNumAllowed;
00053 boolean recNumAccess;
00054 boolean btreeRecNumDb;
00055 boolean btreeRecNumAccess;
00056 boolean recNumRenumber;
00057 boolean keysRenumbered;
00058 boolean dupsAllowed;
00059 boolean dupsOrdered;
00060 boolean transactional;
00061 boolean readUncommittedAllowed;
00062
00067 DataView(Database database, EntryBinding keyBinding,
00068 EntryBinding valueBinding, EntityBinding entityBinding,
00069 boolean writeAllowed, PrimaryKeyAssigner keyAssigner)
00070 throws IllegalArgumentException {
00071
00072 if (database == null) {
00073 throw new IllegalArgumentException("database is null");
00074 }
00075 db = database;
00076 try {
00077 currentTxn =
00078 CurrentTransaction.getInstanceInternal(db.getEnvironment());
00079 DatabaseConfig dbConfig;
00080 if (db instanceof SecondaryDatabase) {
00081 secDb = (SecondaryDatabase) database;
00082 SecondaryConfig secConfig = secDb.getSecondaryConfig();
00083 secKeyCreator = secConfig.getKeyCreator();
00084 dbConfig = secConfig;
00085 } else {
00086 dbConfig = db.getConfig();
00087 }
00088 ordered = !DbCompat.isTypeHash(dbConfig);
00089 recNumAllowed = DbCompat.isTypeQueue(dbConfig) ||
00090 DbCompat.isTypeRecno(dbConfig) ||
00091 DbCompat.getBtreeRecordNumbers(dbConfig);
00092 recNumRenumber = DbCompat.getRenumbering(dbConfig);
00093 dupsAllowed = DbCompat.getSortedDuplicates(dbConfig) ||
00094 DbCompat.getUnsortedDuplicates(dbConfig);
00095 dupsOrdered = DbCompat.getSortedDuplicates(dbConfig);
00096 transactional = currentTxn.isTxnMode() &&
00097 dbConfig.getTransactional();
00098 readUncommittedAllowed = DbCompat.getReadUncommitted(dbConfig);
00099 btreeRecNumDb = recNumAllowed && DbCompat.isTypeBtree(dbConfig);
00100 range = new KeyRange(dbConfig.getBtreeComparator());
00101 } catch (DatabaseException e) {
00102 throw new RuntimeExceptionWrapper(e);
00103 }
00104 this.writeAllowed = writeAllowed;
00105 this.keyBinding = keyBinding;
00106 this.valueBinding = valueBinding;
00107 this.entityBinding = entityBinding;
00108 this.keyAssigner = keyAssigner;
00109 cursorConfig = CursorConfig.DEFAULT;
00110
00111 if (valueBinding != null && entityBinding != null)
00112 throw new IllegalArgumentException(
00113 "both valueBinding and entityBinding are non-null");
00114
00115 if (keyBinding instanceof com.sleepycat.bind.RecordNumberBinding) {
00116 if (!recNumAllowed) {
00117 throw new IllegalArgumentException(
00118 "RecordNumberBinding requires DB_BTREE/DB_RECNUM, " +
00119 "DB_RECNO, or DB_QUEUE");
00120 }
00121 recNumAccess = true;
00122 if (btreeRecNumDb) {
00123 btreeRecNumAccess = true;
00124 }
00125 }
00126 keysRenumbered = recNumRenumber || btreeRecNumAccess;
00127 }
00128
00132 private DataView cloneView() {
00133
00134 try {
00135 return (DataView) super.clone();
00136 } catch (CloneNotSupportedException willNeverOccur) {
00137 throw new IllegalStateException();
00138 }
00139 }
00140
00147 DataView keySetView() {
00148
00149 if (keyBinding == null) {
00150 throw new UnsupportedOperationException("must have keyBinding");
00151 }
00152 DataView view = cloneView();
00153 view.valueBinding = null;
00154 view.entityBinding = null;
00155 return view;
00156 }
00157
00164 DataView valueSetView() {
00165
00166 if (valueBinding == null && entityBinding == null) {
00167 throw new UnsupportedOperationException(
00168 "must have valueBinding or entityBinding");
00169 }
00170 DataView view = cloneView();
00171 view.keyBinding = null;
00172 return view;
00173 }
00174
00187 DataView valueSetView(Object singleKey)
00188 throws DatabaseException, KeyRangeException {
00189
00190
00191
00192
00193
00194 KeyRange singleKeyRange = subRange(singleKey);
00195 DataView view = valueSetView();
00196 view.range = singleKeyRange;
00197 return view;
00198 }
00199
00204 DataView subView(Object beginKey, boolean beginInclusive,
00205 Object endKey, boolean endInclusive,
00206 EntryBinding keyBinding)
00207 throws DatabaseException, KeyRangeException {
00208
00209 DataView view = cloneView();
00210 view.setRange(beginKey, beginInclusive, endKey, endInclusive);
00211 if (keyBinding != null) view.keyBinding = keyBinding;
00212 return view;
00213 }
00214
00218 DataView configuredView(CursorConfig config) {
00219
00220 DataView view = cloneView();
00221 view.cursorConfig = (config != null) ?
00222 DbCompat.cloneCursorConfig(config) : CursorConfig.DEFAULT;
00223 return view;
00224 }
00225
00230 CurrentTransaction getCurrentTxn() {
00231
00232 return transactional ? currentTxn : null;
00233 }
00234
00238 private void setRange(Object beginKey, boolean beginInclusive,
00239 Object endKey, boolean endInclusive)
00240 throws DatabaseException, KeyRangeException {
00241
00242 range = subRange(beginKey, beginInclusive, endKey, endInclusive);
00243 }
00244
00249 DatabaseEntry getSingleKeyThang() {
00250
00251 return range.getSingleKey();
00252 }
00253
00257 final Environment getEnv() {
00258
00259 return currentTxn.getEnvironment();
00260 }
00261
00266 final boolean isSecondary() {
00267
00268 return (secDb != null);
00269 }
00270
00274 boolean isEmpty()
00275 throws DatabaseException {
00276
00277 DataCursor cursor = new DataCursor(this, false);
00278 try {
00279 return cursor.getFirst(false) != OperationStatus.SUCCESS;
00280 } finally {
00281 cursor.close();
00282 }
00283 }
00284
00289 OperationStatus append(Object value, Object[] retPrimaryKey,
00290 Object[] retValue)
00291 throws DatabaseException {
00292
00293
00294
00295
00296
00297
00298
00299
00300 DatabaseEntry keyThang = new DatabaseEntry();
00301 DatabaseEntry valueThang = new DatabaseEntry();
00302 useValue(value, valueThang, null);
00303 OperationStatus status;
00304 if (keyAssigner != null) {
00305 keyAssigner.assignKey(keyThang);
00306 if (!range.check(keyThang)) {
00307 throw new IllegalArgumentException(
00308 "assigned key out of range");
00309 }
00310 DataCursor cursor = new DataCursor(this, true);
00311 try {
00312 status = cursor.getCursor().putNoOverwrite(keyThang,
00313 valueThang);
00314 } finally {
00315 cursor.close();
00316 }
00317 } else {
00318
00319 if (currentTxn.isCDBCursorOpen(db)) {
00320 throw new IllegalStateException(
00321 "cannot open CDB write cursor when read cursor is open");
00322 }
00323 status = DbCompat.append(db, useTransaction(),
00324 keyThang, valueThang);
00325 if (status == OperationStatus.SUCCESS && !range.check(keyThang)) {
00326 db.delete(useTransaction(), keyThang);
00327 throw new IllegalArgumentException(
00328 "appended record number out of range");
00329 }
00330 }
00331 if (status == OperationStatus.SUCCESS) {
00332 returnPrimaryKeyAndValue(keyThang, valueThang,
00333 retPrimaryKey, retValue);
00334 }
00335 return status;
00336 }
00337
00342 Transaction useTransaction() {
00343 return transactional ? currentTxn.getTransaction() : null;
00344 }
00345
00349 void clear()
00350 throws DatabaseException {
00351
00352 DataCursor cursor = new DataCursor(this, true);
00353 try {
00354 OperationStatus status = OperationStatus.SUCCESS;
00355 while (status == OperationStatus.SUCCESS) {
00356 if (keysRenumbered) {
00357 status = cursor.getFirst(true);
00358 } else {
00359 status = cursor.getNext(true);
00360 }
00361 if (status == OperationStatus.SUCCESS) {
00362 cursor.delete();
00363 }
00364 }
00365 } finally {
00366 cursor.close();
00367 }
00368 }
00369
00374 DataCursor join(DataView[] indexViews, Object[] indexKeys,
00375 JoinConfig joinConfig)
00376 throws DatabaseException {
00377
00378 DataCursor joinCursor = null;
00379 DataCursor[] indexCursors = new DataCursor[indexViews.length];
00380 try {
00381 for (int i = 0; i < indexViews.length; i += 1) {
00382 indexCursors[i] = new DataCursor(indexViews[i], false);
00383 indexCursors[i].getSearchKey(indexKeys[i], null, false);
00384 }
00385 joinCursor = new DataCursor(this, indexCursors, joinConfig, true);
00386 return joinCursor;
00387 } finally {
00388 if (joinCursor == null) {
00389
00390 for (int i = 0; i < indexCursors.length; i += 1) {
00391 if (indexCursors[i] != null) {
00392 try { indexCursors[i].close(); }
00393 catch (Exception e) {
00394
00395 }
00396 }
00397 }
00398 }
00399 }
00400 }
00401
00406 DataCursor join(DataCursor[] indexCursors, JoinConfig joinConfig)
00407 throws DatabaseException {
00408
00409 return new DataCursor(this, indexCursors, joinConfig, false);
00410 }
00411
00415 private void returnPrimaryKeyAndValue(DatabaseEntry keyThang,
00416 DatabaseEntry valueThang,
00417 Object[] retPrimaryKey,
00418 Object[] retValue)
00419 throws DatabaseException {
00420
00421
00422
00423
00424 if (retPrimaryKey != null) {
00425 if (keyBinding == null) {
00426 throw new IllegalArgumentException(
00427 "returning key requires primary key binding");
00428 } else if (isSecondary()) {
00429 throw new IllegalArgumentException(
00430 "returning key requires unindexed view");
00431 } else {
00432 retPrimaryKey[0] = keyBinding.entryToObject(keyThang);
00433 }
00434 }
00435 if (retValue != null) {
00436 retValue[0] = makeValue(keyThang, valueThang);
00437 }
00438 }
00439
00443 boolean useKey(Object key, Object value, DatabaseEntry keyThang,
00444 KeyRange checkRange)
00445 throws DatabaseException {
00446
00447 if (key != null) {
00448 if (keyBinding == null) {
00449 throw new IllegalArgumentException(
00450 "non-null key with null key binding");
00451 }
00452 keyBinding.objectToEntry(key, keyThang);
00453 } else {
00454 if (value == null) {
00455 throw new IllegalArgumentException(
00456 "null key and null value");
00457 }
00458 if (entityBinding == null) {
00459 throw new IllegalStateException(
00460 "EntityBinding required to derive key from value");
00461 }
00462 if (isSecondary()) {
00463 DatabaseEntry primaryKeyThang = new DatabaseEntry();
00464 entityBinding.objectToKey(value, primaryKeyThang);
00465 DatabaseEntry valueThang = new DatabaseEntry();
00466 entityBinding.objectToData(value, valueThang);
00467 secKeyCreator.createSecondaryKey(secDb, primaryKeyThang,
00468 valueThang, keyThang);
00469 } else {
00470 entityBinding.objectToKey(value, keyThang);
00471 }
00472 }
00473 if (recNumAccess && DbCompat.getRecordNumber(keyThang) <= 0) {
00474 return false;
00475 }
00476 if (checkRange != null && !checkRange.check(keyThang)) {
00477 return false;
00478 }
00479 return true;
00480 }
00481
00487 final boolean canDeriveKeyFromValue() {
00488
00489 return (entityBinding != null);
00490 }
00491
00496 void useValue(Object value, DatabaseEntry valueThang,
00497 DatabaseEntry checkKeyThang)
00498 throws DatabaseException {
00499
00500 if (value != null) {
00501 if (valueBinding != null) {
00502 valueBinding.objectToEntry(value, valueThang);
00503 } else if (entityBinding != null) {
00504 entityBinding.objectToData(value, valueThang);
00505 if (checkKeyThang != null) {
00506 DatabaseEntry thang = new DatabaseEntry();
00507 entityBinding.objectToKey(value, thang);
00508 if (!KeyRange.equalBytes(thang, checkKeyThang)) {
00509 throw new IllegalArgumentException(
00510 "cannot change primary key");
00511 }
00512 }
00513 } else {
00514 throw new IllegalArgumentException(
00515 "non-null value with null value/entity binding");
00516 }
00517 } else {
00518 valueThang.setData(new byte[0]);
00519 valueThang.setOffset(0);
00520 valueThang.setSize(0);
00521 }
00522 }
00523
00527 Object makeKey(DatabaseEntry keyThang)
00528 throws DatabaseException {
00529
00530 if (keyThang.getSize() == 0) return null;
00531 return keyBinding.entryToObject(keyThang);
00532 }
00533
00537 Object makeValue(DatabaseEntry primaryKeyThang, DatabaseEntry valueThang)
00538 throws DatabaseException {
00539
00540 Object value;
00541 if (valueBinding != null) {
00542 value = valueBinding.entryToObject(valueThang);
00543 } else if (entityBinding != null) {
00544 value = entityBinding.entryToObject(primaryKeyThang,
00545 valueThang);
00546 } else {
00547 throw new UnsupportedOperationException(
00548 "requires valueBinding or entityBinding");
00549 }
00550 return value;
00551 }
00552
00556 KeyRange subRange(Object singleKey)
00557 throws DatabaseException, KeyRangeException {
00558
00559 return range.subRange(makeRangeKey(singleKey));
00560 }
00561
00565 KeyRange subRange(Object beginKey, boolean beginInclusive,
00566 Object endKey, boolean endInclusive)
00567 throws DatabaseException, KeyRangeException {
00568
00569 if (beginKey == endKey && beginInclusive && endInclusive) {
00570 return subRange(beginKey);
00571 }
00572 if (!ordered) {
00573 throw new UnsupportedOperationException(
00574 "Cannot use key ranges on an unsorted database");
00575 }
00576 DatabaseEntry beginThang =
00577 (beginKey != null) ? makeRangeKey(beginKey) : null;
00578 DatabaseEntry endThang =
00579 (endKey != null) ? makeRangeKey(endKey) : null;
00580
00581 return range.subRange(beginThang, beginInclusive,
00582 endThang, endInclusive);
00583 }
00584
00588 private DatabaseEntry makeRangeKey(Object key)
00589 throws DatabaseException {
00590
00591 DatabaseEntry thang = new DatabaseEntry();
00592 if (keyBinding != null) {
00593 useKey(key, null, thang, null);
00594 } else {
00595 useKey(null, key, thang, null);
00596 }
00597 return thang;
00598 }
00599 }