1 8 9 package com.sleepycat.collections; 10 11 import com.sleepycat.bind.EntityBinding; 12 import com.sleepycat.bind.EntryBinding; 13 import com.sleepycat.compat.DbCompat; 14 import com.sleepycat.je.CursorConfig; 15 import com.sleepycat.je.Database; 16 import com.sleepycat.je.DatabaseConfig; 17 import com.sleepycat.je.DatabaseEntry; 18 import com.sleepycat.je.DatabaseException; 19 import com.sleepycat.je.Environment; 20 import com.sleepycat.je.JoinConfig; 21 import com.sleepycat.je.OperationStatus; 22 import com.sleepycat.je.SecondaryConfig; 23 import com.sleepycat.je.SecondaryDatabase; 24 import com.sleepycat.je.SecondaryKeyCreator; 25 import com.sleepycat.je.Transaction; 26 import com.sleepycat.util.RuntimeExceptionWrapper; 27 import com.sleepycat.util.keyrange.KeyRange; 28 import com.sleepycat.util.keyrange.KeyRangeException; 29 30 39 final class DataView implements Cloneable { 40 41 Database db; 42 SecondaryDatabase secDb; 43 CurrentTransaction currentTxn; 44 KeyRange range; 45 EntryBinding keyBinding; 46 EntryBinding valueBinding; 47 EntityBinding entityBinding; 48 PrimaryKeyAssigner keyAssigner; 49 SecondaryKeyCreator secKeyCreator; 50 CursorConfig cursorConfig; boolean writeAllowed; boolean ordered; boolean recNumAllowed; boolean recNumAccess; boolean btreeRecNumDb; boolean btreeRecNumAccess; boolean recNumRenumber; boolean keysRenumbered; boolean dupsAllowed; boolean dupsOrdered; boolean transactional; boolean readUncommittedAllowed; 64 70 DatabaseEntry dupsKey; 71 boolean dupsView; 72 KeyRange dupsRange; 73 74 78 DataView(Database database, EntryBinding keyBinding, 79 EntryBinding valueBinding, EntityBinding entityBinding, 80 boolean writeAllowed, PrimaryKeyAssigner keyAssigner) 81 throws IllegalArgumentException { 82 83 if (database == null) { 84 throw new IllegalArgumentException ("database is null"); 85 } 86 db = database; 87 try { 88 currentTxn = 89 CurrentTransaction.getInstanceInternal(db.getEnvironment()); 90 DatabaseConfig dbConfig; 91 if (db instanceof SecondaryDatabase) { 92 secDb = (SecondaryDatabase) database; 93 SecondaryConfig secConfig = secDb.getSecondaryConfig(); 94 secKeyCreator = secConfig.getKeyCreator(); 95 dbConfig = secConfig; 96 } else { 97 dbConfig = db.getConfig(); 98 } 99 ordered = !DbCompat.isTypeHash(dbConfig); 100 recNumAllowed = DbCompat.isTypeQueue(dbConfig) || 101 DbCompat.isTypeRecno(dbConfig) || 102 DbCompat.getBtreeRecordNumbers(dbConfig); 103 recNumRenumber = DbCompat.getRenumbering(dbConfig); 104 dupsAllowed = DbCompat.getSortedDuplicates(dbConfig) || 105 DbCompat.getUnsortedDuplicates(dbConfig); 106 dupsOrdered = DbCompat.getSortedDuplicates(dbConfig); 107 transactional = currentTxn.isTxnMode() && 108 dbConfig.getTransactional(); 109 readUncommittedAllowed = DbCompat.getReadUncommitted(dbConfig); 110 btreeRecNumDb = recNumAllowed && DbCompat.isTypeBtree(dbConfig); 111 range = new KeyRange(dbConfig.getBtreeComparator()); 112 } catch (DatabaseException e) { 113 throw new RuntimeExceptionWrapper(e); 114 } 115 this.writeAllowed = writeAllowed; 116 this.keyBinding = keyBinding; 117 this.valueBinding = valueBinding; 118 this.entityBinding = entityBinding; 119 this.keyAssigner = keyAssigner; 120 cursorConfig = CursorConfig.DEFAULT; 121 122 if (valueBinding != null && entityBinding != null) 123 throw new IllegalArgumentException ( 124 "both valueBinding and entityBinding are non-null"); 125 126 if (keyBinding instanceof com.sleepycat.bind.RecordNumberBinding) { 127 if (!recNumAllowed) { 128 throw new IllegalArgumentException ( 129 "RecordNumberBinding requires DB_BTREE/DB_RECNUM, " + 130 "DB_RECNO, or DB_QUEUE"); 131 } 132 recNumAccess = true; 133 if (btreeRecNumDb) { 134 btreeRecNumAccess = true; 135 } 136 } 137 keysRenumbered = recNumRenumber || btreeRecNumAccess; 138 } 139 140 143 private DataView cloneView() { 144 145 try { 146 return (DataView) super.clone(); 147 } catch (CloneNotSupportedException willNeverOccur) { 148 throw new IllegalStateException (); 149 } 150 } 151 152 158 DataView keySetView() { 159 160 if (keyBinding == null) { 161 throw new UnsupportedOperationException ("must have keyBinding"); 162 } 163 DataView view = cloneView(); 164 view.valueBinding = null; 165 view.entityBinding = null; 166 return view; 167 } 168 169 175 DataView valueSetView() { 176 177 if (valueBinding == null && entityBinding == null) { 178 throw new UnsupportedOperationException ( 179 "must have valueBinding or entityBinding"); 180 } 181 DataView view = cloneView(); 182 view.keyBinding = null; 183 return view; 184 } 185 186 198 DataView valueSetView(Object singleKey) 199 throws DatabaseException, KeyRangeException { 200 201 205 KeyRange singleKeyRange = subRange(range, singleKey); 206 DataView view = valueSetView(); 207 view.range = singleKeyRange; 208 return view; 209 } 210 211 215 DataView subView(Object beginKey, boolean beginInclusive, 216 Object endKey, boolean endInclusive, 217 EntryBinding keyBinding) 218 throws DatabaseException, KeyRangeException { 219 220 DataView view = cloneView(); 221 view.setRange(beginKey, beginInclusive, endKey, endInclusive); 222 if (keyBinding != null) view.keyBinding = keyBinding; 223 return view; 224 } 225 226 229 DataView duplicatesView(Object secondaryKey, 230 EntryBinding primaryKeyBinding) 231 throws DatabaseException, KeyRangeException { 232 233 if (!isSecondary()) { 234 throw new UnsupportedOperationException 235 ("Only allowed for maps on secondary databases"); 236 } 237 if (dupsView) { 238 throw new IllegalStateException (); 239 } 240 DataView view = cloneView(); 241 view.range = subRange(view.range, secondaryKey); 242 view.dupsKey = view.range.getSingleKey(); 243 view.dupsView = true; 244 view.keyBinding = primaryKeyBinding; 245 return view; 246 } 247 248 251 DataView configuredView(CursorConfig config) { 252 253 DataView view = cloneView(); 254 view.cursorConfig = (config != null) ? 255 DbCompat.cloneCursorConfig(config) : CursorConfig.DEFAULT; 256 return view; 257 } 258 259 263 CurrentTransaction getCurrentTxn() { 264 265 return transactional ? currentTxn : null; 266 } 267 268 271 private void setRange(Object beginKey, boolean beginInclusive, 272 Object endKey, boolean endInclusive) 273 throws DatabaseException, KeyRangeException { 274 275 KeyRange useRange = useSubRange(); 276 useRange = subRange 277 (useRange, beginKey, beginInclusive, endKey, endInclusive); 278 if (dupsView) { 279 dupsRange = useRange; 280 } else { 281 range = useRange; 282 } 283 } 284 285 289 DatabaseEntry getSingleKeyThang() { 290 291 return range.getSingleKey(); 292 } 293 294 297 final Environment getEnv() { 298 299 return currentTxn.getEnvironment(); 300 } 301 302 306 final boolean isSecondary() { 307 308 return (secDb != null); 309 } 310 311 314 boolean isEmpty() 315 throws DatabaseException { 316 317 DataCursor cursor = new DataCursor(this, false); 318 try { 319 return cursor.getFirst(false) != OperationStatus.SUCCESS; 320 } finally { 321 cursor.close(); 322 } 323 } 324 325 329 OperationStatus append(Object value, Object [] retPrimaryKey, 330 Object [] retValue) 331 throws DatabaseException { 332 333 340 DatabaseEntry keyThang = new DatabaseEntry(); 341 DatabaseEntry valueThang = new DatabaseEntry(); 342 useValue(value, valueThang, null); 343 OperationStatus status; 344 if (keyAssigner != null) { 345 keyAssigner.assignKey(keyThang); 346 if (!range.check(keyThang)) { 347 throw new IllegalArgumentException ( 348 "assigned key out of range"); 349 } 350 DataCursor cursor = new DataCursor(this, true); 351 try { 352 status = cursor.getCursor().putNoOverwrite(keyThang, 353 valueThang); 354 } finally { 355 cursor.close(); 356 } 357 } else { 358 359 if (currentTxn.isCDBCursorOpen(db)) { 360 throw new IllegalStateException ( 361 "cannot open CDB write cursor when read cursor is open"); 362 } 363 status = DbCompat.append(db, useTransaction(), 364 keyThang, valueThang); 365 if (status == OperationStatus.SUCCESS && !range.check(keyThang)) { 366 db.delete(useTransaction(), keyThang); 367 throw new IllegalArgumentException ( 368 "appended record number out of range"); 369 } 370 } 371 if (status == OperationStatus.SUCCESS) { 372 returnPrimaryKeyAndValue(keyThang, valueThang, 373 retPrimaryKey, retValue); 374 } 375 return status; 376 } 377 378 382 Transaction useTransaction() { 383 return transactional ? currentTxn.getTransaction() : null; 384 } 385 386 389 void clear() 390 throws DatabaseException { 391 392 DataCursor cursor = new DataCursor(this, true); 393 try { 394 OperationStatus status = OperationStatus.SUCCESS; 395 while (status == OperationStatus.SUCCESS) { 396 if (keysRenumbered) { 397 status = cursor.getFirst(true); 398 } else { 399 status = cursor.getNext(true); 400 } 401 if (status == OperationStatus.SUCCESS) { 402 cursor.delete(); 403 } 404 } 405 } finally { 406 cursor.close(); 407 } 408 } 409 410 414 DataCursor join(DataView[] indexViews, Object [] indexKeys, 415 JoinConfig joinConfig) 416 throws DatabaseException { 417 418 DataCursor joinCursor = null; 419 DataCursor[] indexCursors = new DataCursor[indexViews.length]; 420 try { 421 for (int i = 0; i < indexViews.length; i += 1) { 422 indexCursors[i] = new DataCursor(indexViews[i], false); 423 indexCursors[i].getSearchKey(indexKeys[i], null, false); 424 } 425 joinCursor = new DataCursor(this, indexCursors, joinConfig, true); 426 return joinCursor; 427 } finally { 428 if (joinCursor == null) { 429 for (int i = 0; i < indexCursors.length; i += 1) { 431 if (indexCursors[i] != null) { 432 try { indexCursors[i].close(); } 433 catch (Exception e) { 434 435 } 436 } 437 } 438 } 439 } 440 } 441 442 446 DataCursor join(DataCursor[] indexCursors, JoinConfig joinConfig) 447 throws DatabaseException { 448 449 return new DataCursor(this, indexCursors, joinConfig, false); 450 } 451 452 455 private void returnPrimaryKeyAndValue(DatabaseEntry keyThang, 456 DatabaseEntry valueThang, 457 Object [] retPrimaryKey, 458 Object [] retValue) 459 throws DatabaseException { 460 461 464 if (retPrimaryKey != null) { 465 if (keyBinding == null) { 466 throw new IllegalArgumentException ( 467 "returning key requires primary key binding"); 468 } else if (isSecondary()) { 469 throw new IllegalArgumentException ( 470 "returning key requires unindexed view"); 471 } else { 472 retPrimaryKey[0] = keyBinding.entryToObject(keyThang); 473 } 474 } 475 if (retValue != null) { 476 retValue[0] = makeValue(keyThang, valueThang); 477 } 478 } 479 480 483 boolean useKey(Object key, Object value, DatabaseEntry keyThang, 484 KeyRange checkRange) 485 throws DatabaseException { 486 487 if (key != null) { 488 if (keyBinding == null) { 489 throw new IllegalArgumentException ( 490 "non-null key with null key binding"); 491 } 492 keyBinding.objectToEntry(key, keyThang); 493 } else { 494 if (value == null) { 495 throw new IllegalArgumentException ( 496 "null key and null value"); 497 } 498 if (entityBinding == null) { 499 throw new IllegalStateException ( 500 "EntityBinding required to derive key from value"); 501 } 502 if (!dupsView && isSecondary()) { 503 DatabaseEntry primaryKeyThang = new DatabaseEntry(); 504 entityBinding.objectToKey(value, primaryKeyThang); 505 DatabaseEntry valueThang = new DatabaseEntry(); 506 entityBinding.objectToData(value, valueThang); 507 secKeyCreator.createSecondaryKey(secDb, primaryKeyThang, 508 valueThang, keyThang); 509 } else { 510 entityBinding.objectToKey(value, keyThang); 511 } 512 } 513 if (recNumAccess && DbCompat.getRecordNumber(keyThang) <= 0) { 514 return false; 515 } 516 if (checkRange != null && !checkRange.check(keyThang)) { 517 return false; 518 } 519 return true; 520 } 521 522 527 final boolean canDeriveKeyFromValue() { 528 529 return (entityBinding != null); 530 } 531 532 536 void useValue(Object value, DatabaseEntry valueThang, 537 DatabaseEntry checkKeyThang) 538 throws DatabaseException { 539 540 if (value != null) { 541 if (valueBinding != null) { 542 valueBinding.objectToEntry(value, valueThang); 543 } else if (entityBinding != null) { 544 entityBinding.objectToData(value, valueThang); 545 if (checkKeyThang != null) { 546 DatabaseEntry thang = new DatabaseEntry(); 547 entityBinding.objectToKey(value, thang); 548 if (!KeyRange.equalBytes(thang, checkKeyThang)) { 549 throw new IllegalArgumentException ( 550 "cannot change primary key"); 551 } 552 } 553 } else { 554 throw new IllegalArgumentException ( 555 "non-null value with null value/entity binding"); 556 } 557 } else { 558 valueThang.setData(KeyRange.ZERO_LENGTH_BYTE_ARRAY); 559 valueThang.setOffset(0); 560 valueThang.setSize(0); 561 } 562 } 563 564 567 Object makeKey(DatabaseEntry keyThang, DatabaseEntry priKeyThang) { 568 569 if (keyBinding == null) { 570 throw new UnsupportedOperationException (); 571 } else { 572 DatabaseEntry thang = dupsView ? priKeyThang : keyThang; 573 if (thang.getSize() == 0) { 574 return null; 575 } else { 576 return keyBinding.entryToObject(thang); 577 } 578 } 579 } 580 581 584 Object makeValue(DatabaseEntry primaryKeyThang, DatabaseEntry valueThang) { 585 586 Object value; 587 if (valueBinding != null) { 588 value = valueBinding.entryToObject(valueThang); 589 } else if (entityBinding != null) { 590 value = entityBinding.entryToObject(primaryKeyThang, 591 valueThang); 592 } else { 593 throw new UnsupportedOperationException ( 594 "requires valueBinding or entityBinding"); 595 } 596 return value; 597 } 598 599 602 KeyRange subRange(KeyRange useRange, Object singleKey) 603 throws DatabaseException, KeyRangeException { 604 605 return useRange.subRange(makeRangeKey(singleKey)); 606 } 607 608 611 KeyRange subRange(KeyRange useRange, 612 Object beginKey, boolean beginInclusive, 613 Object endKey, boolean endInclusive) 614 throws DatabaseException, KeyRangeException { 615 616 if (beginKey == endKey && beginInclusive && endInclusive) { 617 return subRange(useRange, beginKey); 618 } 619 if (!ordered) { 620 throw new UnsupportedOperationException ( 621 "Cannot use key ranges on an unsorted database"); 622 } 623 DatabaseEntry beginThang = 624 (beginKey != null) ? makeRangeKey(beginKey) : null; 625 DatabaseEntry endThang = 626 (endKey != null) ? makeRangeKey(endKey) : null; 627 628 return useRange.subRange(beginThang, beginInclusive, 629 endThang, endInclusive); 630 } 631 632 637 KeyRange useSubRange() 638 throws DatabaseException { 639 640 if (dupsView) { 641 synchronized (this) { 642 if (dupsRange == null) { 643 DatabaseConfig config = 644 secDb.getPrimaryDatabase().getConfig(); 645 dupsRange = new KeyRange(config.getBtreeComparator()); 646 } 647 } 648 return dupsRange; 649 } else { 650 return range; 651 } 652 } 653 654 657 private DatabaseEntry makeRangeKey(Object key) 658 throws DatabaseException { 659 660 DatabaseEntry thang = new DatabaseEntry(); 661 if (keyBinding != null) { 662 useKey(key, null, thang, null); 663 } else { 664 useKey(null, key, thang, null); 665 } 666 return thang; 667 } 668 } 669 | Popular Tags |