001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.backends.jeb; 028 import org.opends.messages.Message; 029 030 import java.util.*; 031 032 import com.sleepycat.je.*; 033 034 import org.opends.server.api.SubstringMatchingRule; 035 import org.opends.server.api.OrderingMatchingRule; 036 import org.opends.server.api.ApproximateMatchingRule; 037 import org.opends.server.protocols.asn1.ASN1OctetString; 038 039 import static org.opends.server.loggers.debug.DebugLogger.*; 040 import org.opends.server.loggers.debug.DebugTracer; 041 import org.opends.server.types.*; 042 import org.opends.server.admin.std.server.LocalDBIndexCfg; 043 import org.opends.server.admin.std.meta.LocalDBIndexCfgDefn; 044 import org.opends.server.admin.server.ConfigurationChangeListener; 045 import org.opends.server.config.ConfigException; 046 import static org.opends.messages.JebMessages.*; 047 048 import org.opends.server.core.DirectoryServer; 049 import org.opends.server.util.StaticUtils; 050 051 /** 052 * Class representing an attribute index. 053 * We have a separate database for each type of indexing, which makes it easy 054 * to tell which attribute indexes are configured. The different types of 055 * indexing are equality, presence, substrings and ordering. The keys in the 056 * ordering index are ordered by setting the btree comparator to the ordering 057 * matching rule comparator. 058 * Note that the values in the equality index are normalized by the equality 059 * matching rule, whereas the values in the ordering index are normalized 060 * by the ordering matching rule. If these could be guaranteed to be identical 061 * then we would not need a separate ordering index. 062 */ 063 public class AttributeIndex 064 implements ConfigurationChangeListener<LocalDBIndexCfg> 065 { 066 /** 067 * The tracer object for the debug logger. 068 */ 069 private static final DebugTracer TRACER = getTracer(); 070 071 072 073 /** 074 * A database key for the presence index. 075 */ 076 public static final DatabaseEntry presenceKey = 077 new DatabaseEntry("+".getBytes()); 078 079 /** 080 * The entryContainer in which this attribute index resides. 081 */ 082 private EntryContainer entryContainer; 083 084 private Environment env; 085 086 /** 087 * The attribute index configuration. 088 */ 089 private LocalDBIndexCfg indexConfig; 090 091 /** 092 * The index database for attribute equality. 093 */ 094 Index equalityIndex = null; 095 096 /** 097 * The index database for attribute presence. 098 */ 099 Index presenceIndex = null; 100 101 /** 102 * The index database for attribute substrings. 103 */ 104 Index substringIndex = null; 105 106 /** 107 * The index database for attribute ordering. 108 */ 109 Index orderingIndex = null; 110 111 /** 112 * The index database for attribute approximate. 113 */ 114 Index approximateIndex = null; 115 116 private State state; 117 118 private int cursorEntryLimit = 100000; 119 120 /** 121 * Create a new attribute index object. 122 * @param entryContainer The entryContainer of this attribute index. 123 * @param state The state database to persist index state info. 124 * @param env The JE environment handle. 125 * @param indexConfig The attribute index configuration. 126 * @throws DatabaseException if a JE database error occurs. 127 * @throws ConfigException if a configuration related error occurs. 128 */ 129 public AttributeIndex(LocalDBIndexCfg indexConfig, State state, 130 Environment env, 131 EntryContainer entryContainer) 132 throws DatabaseException, ConfigException 133 { 134 this.entryContainer = entryContainer; 135 this.env = env; 136 this.indexConfig = indexConfig; 137 this.state = state; 138 139 AttributeType attrType = indexConfig.getAttribute(); 140 String name = 141 entryContainer.getDatabasePrefix() + "_" + attrType.getNameOrOID(); 142 int indexEntryLimit = indexConfig.getIndexEntryLimit(); 143 144 if (indexConfig.getIndexType().contains( 145 LocalDBIndexCfgDefn.IndexType.EQUALITY)) 146 { 147 if (attrType.getEqualityMatchingRule() == null) 148 { 149 Message message = ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get( 150 String.valueOf(attrType), "equality"); 151 throw new ConfigException(message); 152 } 153 154 Indexer equalityIndexer = new EqualityIndexer(attrType); 155 this.equalityIndex = new Index(name + ".equality", 156 equalityIndexer, 157 state, 158 indexEntryLimit, 159 cursorEntryLimit, 160 false, 161 env, 162 entryContainer); 163 } 164 165 if (indexConfig.getIndexType().contains( 166 LocalDBIndexCfgDefn.IndexType.PRESENCE)) 167 { 168 Indexer presenceIndexer = new PresenceIndexer(attrType); 169 this.presenceIndex = new Index(name + ".presence", 170 presenceIndexer, 171 state, 172 indexEntryLimit, 173 cursorEntryLimit, 174 false, 175 env, 176 entryContainer); 177 } 178 179 if (indexConfig.getIndexType().contains( 180 LocalDBIndexCfgDefn.IndexType.SUBSTRING)) 181 { 182 if (attrType.getSubstringMatchingRule() == null) 183 { 184 Message message = ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get( 185 String.valueOf(attrType), "substring"); 186 throw new ConfigException(message); 187 } 188 189 Indexer substringIndexer = new SubstringIndexer(attrType, 190 indexConfig.getSubstringLength()); 191 this.substringIndex = new Index(name + ".substring", 192 substringIndexer, 193 state, 194 indexEntryLimit, 195 cursorEntryLimit, 196 false, 197 env, 198 entryContainer); 199 } 200 201 if (indexConfig.getIndexType().contains( 202 LocalDBIndexCfgDefn.IndexType.ORDERING)) 203 { 204 if (attrType.getOrderingMatchingRule() == null) 205 { 206 Message message = ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get( 207 String.valueOf(attrType), "ordering"); 208 throw new ConfigException(message); 209 } 210 211 Indexer orderingIndexer = new OrderingIndexer(attrType); 212 this.orderingIndex = new Index(name + ".ordering", 213 orderingIndexer, 214 state, 215 indexEntryLimit, 216 cursorEntryLimit, 217 false, 218 env, 219 entryContainer); 220 } 221 if (indexConfig.getIndexType().contains( 222 LocalDBIndexCfgDefn.IndexType.APPROXIMATE)) 223 { 224 if (attrType.getApproximateMatchingRule() == null) 225 { 226 Message message = ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get( 227 String.valueOf(attrType), "approximate"); 228 throw new ConfigException(message); 229 } 230 231 Indexer approximateIndexer = new ApproximateIndexer(attrType); 232 this.approximateIndex = new Index(name + ".approximate", 233 approximateIndexer, 234 state, 235 indexEntryLimit, 236 cursorEntryLimit, 237 false, 238 env, 239 entryContainer); 240 } 241 242 this.indexConfig.addChangeListener(this); 243 } 244 245 /** 246 * Open the attribute index. 247 * 248 * @throws DatabaseException if a JE database error occurs while 249 * openning the index. 250 */ 251 public void open() throws DatabaseException 252 { 253 if (equalityIndex != null) 254 { 255 equalityIndex.open(); 256 } 257 258 if (presenceIndex != null) 259 { 260 presenceIndex.open(); 261 } 262 263 if (substringIndex != null) 264 { 265 substringIndex.open(); 266 } 267 268 if (orderingIndex != null) 269 { 270 orderingIndex.open(); 271 } 272 273 if (approximateIndex != null) 274 { 275 approximateIndex.open(); 276 } 277 } 278 279 /** 280 * Close the attribute index. 281 * 282 * @throws DatabaseException if a JE database error occurs while 283 * closing the index. 284 */ 285 public void close() throws DatabaseException 286 { 287 if (equalityIndex != null) 288 { 289 equalityIndex.close(); 290 } 291 292 if (presenceIndex != null) 293 { 294 presenceIndex.close(); 295 } 296 297 if (substringIndex != null) 298 { 299 substringIndex.close(); 300 } 301 302 if (orderingIndex != null) 303 { 304 orderingIndex.close(); 305 } 306 307 if (approximateIndex != null) 308 { 309 approximateIndex.close(); 310 } 311 312 indexConfig.removeChangeListener(this); 313 // The entryContainer is responsible for closing the JE databases. 314 } 315 316 /** 317 * Get the attribute type of this attribute index. 318 * @return The attribute type of this attribute index. 319 */ 320 public AttributeType getAttributeType() 321 { 322 return indexConfig.getAttribute(); 323 } 324 325 /** 326 * Get the JE index configuration used by this index. 327 * @return The configuration in effect. 328 */ 329 public LocalDBIndexCfg getConfiguration() 330 { 331 return indexConfig; 332 } 333 334 /** 335 * Update the attribute index for a new entry. 336 * 337 * @param buffer The index buffer to use to store the added keys 338 * @param entryID The entry ID. 339 * @param entry The contents of the new entry. 340 * @return True if all the index keys for the entry are added. False if the 341 * entry ID already exists for some keys. 342 * @throws DatabaseException If an error occurs in the JE database. 343 * @throws DirectoryException If a Directory Server error occurs. 344 * @throws JebException If an error occurs in the JE backend. 345 */ 346 public boolean addEntry(IndexBuffer buffer, EntryID entryID, 347 Entry entry) 348 throws DatabaseException, DirectoryException, JebException 349 { 350 boolean success = true; 351 352 if (equalityIndex != null) 353 { 354 if(!equalityIndex.addEntry(buffer, entryID, entry)) 355 { 356 success = false; 357 } 358 } 359 360 if (presenceIndex != null) 361 { 362 if(!presenceIndex.addEntry(buffer, entryID, entry)) 363 { 364 success = false; 365 } 366 } 367 368 if (substringIndex != null) 369 { 370 if(!substringIndex.addEntry(buffer, entryID, entry)) 371 { 372 success = false; 373 } 374 } 375 376 if (orderingIndex != null) 377 { 378 if(!orderingIndex.addEntry(buffer, entryID, entry)) 379 { 380 success = false; 381 } 382 } 383 384 if (approximateIndex != null) 385 { 386 if(!approximateIndex.addEntry(buffer, entryID, entry)) 387 { 388 success = false; 389 } 390 } 391 392 return success; 393 } 394 395 396 /** 397 * Update the attribute index for a new entry. 398 * 399 * @param txn The database transaction to be used for the insertions. 400 * @param entryID The entry ID. 401 * @param entry The contents of the new entry. 402 * @return True if all the index keys for the entry are added. False if the 403 * entry ID already exists for some keys. 404 * @throws DatabaseException If an error occurs in the JE database. 405 * @throws DirectoryException If a Directory Server error occurs. 406 * @throws JebException If an error occurs in the JE backend. 407 */ 408 public boolean addEntry(Transaction txn, EntryID entryID, Entry entry) 409 throws DatabaseException, DirectoryException, JebException 410 { 411 boolean success = true; 412 413 if (equalityIndex != null) 414 { 415 if(!equalityIndex.addEntry(txn, entryID, entry)) 416 { 417 success = false; 418 } 419 } 420 421 if (presenceIndex != null) 422 { 423 if(!presenceIndex.addEntry(txn, entryID, entry)) 424 { 425 success = false; 426 } 427 } 428 429 if (substringIndex != null) 430 { 431 if(!substringIndex.addEntry(txn, entryID, entry)) 432 { 433 success = false; 434 } 435 } 436 437 if (orderingIndex != null) 438 { 439 if(!orderingIndex.addEntry(txn, entryID, entry)) 440 { 441 success = false; 442 } 443 } 444 445 if (approximateIndex != null) 446 { 447 if(!approximateIndex.addEntry(txn, entryID, entry)) 448 { 449 success = false; 450 } 451 } 452 453 return success; 454 } 455 456 /** 457 * Update the attribute index for a deleted entry. 458 * 459 * @param buffer The index buffer to use to store the deleted keys 460 * @param entryID The entry ID 461 * @param entry The contents of the deleted entry. 462 * @throws DatabaseException If an error occurs in the JE database. 463 * @throws DirectoryException If a Directory Server error occurs. 464 * @throws JebException If an error occurs in the JE backend. 465 */ 466 public void removeEntry(IndexBuffer buffer, EntryID entryID, 467 Entry entry) 468 throws DatabaseException, DirectoryException, JebException 469 { 470 if (equalityIndex != null) 471 { 472 equalityIndex.removeEntry(buffer, entryID, entry); 473 } 474 475 if (presenceIndex != null) 476 { 477 presenceIndex.removeEntry(buffer, entryID, entry); 478 } 479 480 if (substringIndex != null) 481 { 482 substringIndex.removeEntry(buffer, entryID, entry); 483 } 484 485 if (orderingIndex != null) 486 { 487 orderingIndex.removeEntry(buffer, entryID, entry); 488 } 489 490 if(approximateIndex != null) 491 { 492 approximateIndex.removeEntry(buffer, entryID, entry); 493 } 494 } 495 496 /** 497 * Update the attribute index for a deleted entry. 498 * 499 * @param txn The database transaction to be used for the deletions 500 * @param entryID The entry ID 501 * @param entry The contents of the deleted entry. 502 * @throws DatabaseException If an error occurs in the JE database. 503 * @throws DirectoryException If a Directory Server error occurs. 504 * @throws JebException If an error occurs in the JE backend. 505 */ 506 public void removeEntry(Transaction txn, EntryID entryID, Entry entry) 507 throws DatabaseException, DirectoryException, JebException 508 { 509 if (equalityIndex != null) 510 { 511 equalityIndex.removeEntry(txn, entryID, entry); 512 } 513 514 if (presenceIndex != null) 515 { 516 presenceIndex.removeEntry(txn, entryID, entry); 517 } 518 519 if (substringIndex != null) 520 { 521 substringIndex.removeEntry(txn, entryID, entry); 522 } 523 524 if (orderingIndex != null) 525 { 526 orderingIndex.removeEntry(txn, entryID, entry); 527 } 528 529 if(approximateIndex != null) 530 { 531 approximateIndex.removeEntry(txn, entryID, entry); 532 } 533 } 534 535 /** 536 * Update the index to reflect a sequence of modifications in a Modify 537 * operation. 538 * 539 * @param txn The JE transaction to use for database updates. 540 * @param entryID The ID of the entry that was modified. 541 * @param oldEntry The entry before the modifications were applied. 542 * @param newEntry The entry after the modifications were applied. 543 * @param mods The sequence of modifications in the Modify operation. 544 * @throws DatabaseException If an error occurs during an operation on a 545 * JE database. 546 */ 547 public void modifyEntry(Transaction txn, 548 EntryID entryID, 549 Entry oldEntry, 550 Entry newEntry, 551 List<Modification> mods) 552 throws DatabaseException 553 { 554 if (equalityIndex != null) 555 { 556 equalityIndex.modifyEntry(txn, entryID, oldEntry, newEntry, mods); 557 } 558 559 if (presenceIndex != null) 560 { 561 presenceIndex.modifyEntry(txn, entryID, oldEntry, newEntry, mods); 562 } 563 564 if (substringIndex != null) 565 { 566 substringIndex.modifyEntry(txn, entryID, oldEntry, newEntry, mods); 567 } 568 569 if (orderingIndex != null) 570 { 571 orderingIndex.modifyEntry(txn, entryID, oldEntry, newEntry, mods); 572 } 573 574 if (approximateIndex != null) 575 { 576 approximateIndex.modifyEntry(txn, entryID, oldEntry, newEntry, mods); 577 } 578 } 579 580 /** 581 * Update the index to reflect a sequence of modifications in a Modify 582 * operation. 583 * 584 * @param buffer The index buffer used to buffer up the index changes. 585 * @param entryID The ID of the entry that was modified. 586 * @param oldEntry The entry before the modifications were applied. 587 * @param newEntry The entry after the modifications were applied. 588 * @param mods The sequence of modifications in the Modify operation. 589 * @throws DatabaseException If an error occurs during an operation on a 590 * JE database. 591 */ 592 public void modifyEntry(IndexBuffer buffer, 593 EntryID entryID, 594 Entry oldEntry, 595 Entry newEntry, 596 List<Modification> mods) 597 throws DatabaseException 598 { 599 if (equalityIndex != null) 600 { 601 equalityIndex.modifyEntry(buffer, entryID, oldEntry, newEntry, mods); 602 } 603 604 if (presenceIndex != null) 605 { 606 presenceIndex.modifyEntry(buffer, entryID, oldEntry, newEntry, mods); 607 } 608 609 if (substringIndex != null) 610 { 611 substringIndex.modifyEntry(buffer, entryID, oldEntry, newEntry, mods); 612 } 613 614 if (orderingIndex != null) 615 { 616 orderingIndex.modifyEntry(buffer, entryID, oldEntry, newEntry, mods); 617 } 618 619 if (approximateIndex != null) 620 { 621 approximateIndex.modifyEntry(buffer, entryID, oldEntry, newEntry, mods); 622 } 623 } 624 625 /** 626 * Makes a byte array representing a substring index key for 627 * one substring of a value. 628 * 629 * @param bytes The byte array containing the value. 630 * @param pos The starting position of the substring. 631 * @param len The length of the substring. 632 * @return A byte array containing a substring key. 633 */ 634 private byte[] makeSubstringKey(byte[] bytes, int pos, int len) 635 { 636 byte[] keyBytes = new byte[len]; 637 System.arraycopy(bytes, pos, keyBytes, 0, len); 638 return keyBytes; 639 } 640 641 /** 642 * Decompose an attribute value into a set of substring index keys. 643 * The ID of the entry containing this value should be inserted 644 * into the list of each of these keys. 645 * 646 * @param value A byte array containing the normalized attribute value. 647 * @return A set of index keys. 648 */ 649 Set<ByteString> substringKeys(byte[] value) 650 { 651 // Eliminate duplicates by putting the keys into a set. 652 // Sorting the keys will ensure database record locks are acquired 653 // in a consistent order and help prevent transaction deadlocks between 654 // concurrent writers. 655 Set<ByteString> set = new HashSet<ByteString>(); 656 657 int substrLength = indexConfig.getSubstringLength(); 658 byte[] keyBytes; 659 660 // Example: The value is ABCDE and the substring length is 3. 661 // We produce the keys ABC BCD CDE DE E 662 // To find values containing a short substring such as DE, 663 // iterate through keys with prefix DE. To find values 664 // containing a longer substring such as BCDE, read keys 665 // BCD and CDE. 666 for (int i = 0, remain = value.length; remain > 0; i++, remain--) 667 { 668 int len = Math.min(substrLength, remain); 669 keyBytes = makeSubstringKey(value, i, len); 670 set.add(new ASN1OctetString(keyBytes)); 671 } 672 673 return set; 674 } 675 676 /** 677 * Retrieve the entry IDs that might contain a given substring. 678 * @param bytes A normalized substring of an attribute value. 679 * @return The candidate entry IDs. 680 */ 681 private EntryIDSet matchSubstring(byte[] bytes) 682 { 683 int substrLength = indexConfig.getSubstringLength(); 684 685 // There are two cases, depending on whether the user-provided 686 // substring is smaller than the configured index substring length or not. 687 if (bytes.length < substrLength) 688 { 689 // Iterate through all the keys that have this value as the prefix. 690 691 // Set the lower bound for a range search. 692 byte[] lower = makeSubstringKey(bytes, 0, bytes.length); 693 694 // Set the upper bound for a range search. 695 // We need a key for the upper bound that is of equal length 696 // but slightly greater than the lower bound. 697 byte[] upper = makeSubstringKey(bytes, 0, bytes.length); 698 for (int i = upper.length-1; i >= 0; i--) 699 { 700 if (upper[i] == 0xFF) 701 { 702 // We have to carry the overflow to the more significant byte. 703 upper[i] = 0; 704 } 705 else 706 { 707 // No overflow, we can stop. 708 upper[i] = (byte) (upper[i] + 1); 709 break; 710 } 711 } 712 713 // Read the range: lower <= keys < upper. 714 return substringIndex.readRange(lower, upper, true, false); 715 } 716 else 717 { 718 // Break the value up into fragments of length equal to the 719 // index substring length, and read those keys. 720 721 // Eliminate duplicates by putting the keys into a set. 722 Set<byte[]> set = 723 new TreeSet<byte[]>(substringIndex.indexer.getComparator()); 724 725 // Example: The value is ABCDE and the substring length is 3. 726 // We produce the keys ABC BCD CDE. 727 for (int first = 0, last = substrLength; 728 last <= bytes.length; first++, last++) 729 { 730 byte[] keyBytes; 731 keyBytes = makeSubstringKey(bytes, first, substrLength); 732 set.add(keyBytes); 733 } 734 735 EntryIDSet results = new EntryIDSet(); 736 DatabaseEntry key = new DatabaseEntry(); 737 for (byte[] keyBytes : set) 738 { 739 // Read the key. 740 key.setData(keyBytes); 741 EntryIDSet list = substringIndex.readKey(key, null, LockMode.DEFAULT); 742 743 // Incorporate them into the results. 744 results.retainAll(list); 745 746 // We may have reached the point of diminishing returns where 747 // it is quicker to stop now and process the current small number of 748 // candidates. 749 if (results.isDefined() && 750 results.size() <= IndexFilter.FILTER_CANDIDATE_THRESHOLD) 751 { 752 break; 753 } 754 } 755 756 return results; 757 } 758 } 759 760 /** 761 * Uses an equality index to retrieve the entry IDs that might contain a 762 * given initial substring. 763 * @param bytes A normalized initial substring of an attribute value. 764 * @return The candidate entry IDs. 765 */ 766 private EntryIDSet matchInitialSubstring(byte[] bytes) 767 { 768 // Iterate through all the keys that have this value as the prefix. 769 770 // Set the lower bound for a range search. 771 byte[] lower = bytes; 772 773 // Set the upper bound for a range search. 774 // We need a key for the upper bound that is of equal length 775 // but slightly greater than the lower bound. 776 byte[] upper = new byte[bytes.length]; 777 System.arraycopy(bytes,0, upper, 0, bytes.length); 778 779 for (int i = upper.length-1; i >= 0; i--) 780 { 781 if (upper[i] == 0xFF) 782 { 783 // We have to carry the overflow to the more significant byte. 784 upper[i] = 0; 785 } 786 else 787 { 788 // No overflow, we can stop. 789 upper[i] = (byte) (upper[i] + 1); 790 break; 791 } 792 } 793 794 // Read the range: lower <= keys < upper. 795 return equalityIndex.readRange(lower, upper, true, false); 796 } 797 798 /** 799 * Retrieve the entry IDs that might match an equality filter. 800 * 801 * @param equalityFilter The equality filter. 802 * @param debugBuffer If not null, a diagnostic string will be written 803 * which will help determine how the indexes contributed 804 * to this search. 805 * @return The candidate entry IDs that might contain the filter 806 * assertion value. 807 */ 808 public EntryIDSet evaluateEqualityFilter(SearchFilter equalityFilter, 809 StringBuilder debugBuffer) 810 { 811 if (equalityIndex == null) 812 { 813 return new EntryIDSet(); 814 } 815 816 try 817 { 818 // Make a key from the normalized assertion value. 819 byte[] keyBytes = 820 equalityFilter.getAssertionValue().getNormalizedValue().value(); 821 DatabaseEntry key = new DatabaseEntry(keyBytes); 822 823 if(debugBuffer != null) 824 { 825 debugBuffer.append("[INDEX:"); 826 debugBuffer.append(indexConfig.getAttribute().getNameOrOID()); 827 debugBuffer.append("."); 828 debugBuffer.append("equality]"); 829 } 830 831 // Read the key. 832 return equalityIndex.readKey(key, null, LockMode.DEFAULT); 833 } 834 catch (DirectoryException e) 835 { 836 if (debugEnabled()) 837 { 838 TRACER.debugCaught(DebugLogLevel.ERROR, e); 839 } 840 return new EntryIDSet(); 841 } 842 } 843 844 /** 845 * Retrieve the entry IDs that might match a presence filter. 846 * 847 * @param filter The presence filter. 848 * @param debugBuffer If not null, a diagnostic string will be written 849 * which will help determine how the indexes contributed 850 * to this search. 851 * @return The candidate entry IDs that might contain one or more 852 * values of the attribute type in the filter. 853 */ 854 public EntryIDSet evaluatePresenceFilter(SearchFilter filter, 855 StringBuilder debugBuffer) 856 { 857 if (presenceIndex == null) 858 { 859 return new EntryIDSet(); 860 } 861 862 if(debugBuffer != null) 863 { 864 debugBuffer.append("[INDEX:"); 865 debugBuffer.append(indexConfig.getAttribute().getNameOrOID()); 866 debugBuffer.append("."); 867 debugBuffer.append("presence]"); 868 } 869 870 // Read the presence key 871 return presenceIndex.readKey(presenceKey, null, LockMode.DEFAULT); 872 } 873 874 /** 875 * Retrieve the entry IDs that might match a greater-or-equal filter. 876 * 877 * @param filter The greater-or-equal filter. 878 * @param debugBuffer If not null, a diagnostic string will be written 879 * which will help determine how the indexes contributed 880 * to this search. 881 * @return The candidate entry IDs that might contain a value 882 * greater than or equal to the filter assertion value. 883 */ 884 public EntryIDSet evaluateGreaterOrEqualFilter(SearchFilter filter, 885 StringBuilder debugBuffer) 886 { 887 if (orderingIndex == null) 888 { 889 return new EntryIDSet(); 890 } 891 892 try 893 { 894 // Set the lower bound for a range search. 895 // Use the ordering matching rule to normalize the value. 896 OrderingMatchingRule orderingRule = 897 filter.getAttributeType().getOrderingMatchingRule(); 898 byte[] lower = orderingRule.normalizeValue( 899 filter.getAssertionValue().getValue()).value(); 900 901 // Set the upper bound to 0 to search all keys greater then the lower 902 // bound. 903 byte[] upper = new byte[0]; 904 905 if(debugBuffer != null) 906 { 907 debugBuffer.append("[INDEX:"); 908 debugBuffer.append(indexConfig.getAttribute().getNameOrOID()); 909 debugBuffer.append("."); 910 debugBuffer.append("ordering]"); 911 } 912 913 // Read the range: lower <= keys < upper. 914 return orderingIndex.readRange(lower, upper, true, false); 915 } 916 catch (DirectoryException e) 917 { 918 if (debugEnabled()) 919 { 920 TRACER.debugCaught(DebugLogLevel.ERROR, e); 921 } 922 return new EntryIDSet(); 923 } 924 } 925 926 /** 927 * Retrieve the entry IDs that might match a less-or-equal filter. 928 * 929 * @param filter The less-or-equal filter. 930 * @param debugBuffer If not null, a diagnostic string will be written 931 * which will help determine how the indexes contributed 932 * to this search. 933 * @return The candidate entry IDs that might contain a value 934 * less than or equal to the filter assertion value. 935 */ 936 public EntryIDSet evaluateLessOrEqualFilter(SearchFilter filter, 937 StringBuilder debugBuffer) 938 { 939 if (orderingIndex == null) 940 { 941 return new EntryIDSet(); 942 } 943 944 try 945 { 946 // Set the lower bound to 0 to start the range search from the smallest 947 // key. 948 byte[] lower = new byte[0]; 949 950 // Set the upper bound for a range search. 951 // Use the ordering matching rule to normalize the value. 952 OrderingMatchingRule orderingRule = 953 filter.getAttributeType().getOrderingMatchingRule(); 954 byte[] upper = orderingRule.normalizeValue( 955 filter.getAssertionValue().getValue()).value(); 956 957 if(debugBuffer != null) 958 { 959 debugBuffer.append("[INDEX:"); 960 debugBuffer.append(indexConfig.getAttribute().getNameOrOID()); 961 debugBuffer.append("."); 962 debugBuffer.append("ordering]"); 963 } 964 965 // Read the range: lower < keys <= upper. 966 return orderingIndex.readRange(lower, upper, false, true); 967 } 968 catch (DirectoryException e) 969 { 970 if (debugEnabled()) 971 { 972 TRACER.debugCaught(DebugLogLevel.ERROR, e); 973 } 974 return new EntryIDSet(); 975 } 976 } 977 978 /** 979 * Retrieve the entry IDs that might match a substring filter. 980 * 981 * @param filter The substring filter. 982 * @param debugBuffer If not null, a diagnostic string will be written 983 * which will help determine how the indexes contributed 984 * to this search. 985 * @return The candidate entry IDs that might contain a value 986 * that matches the filter substrings. 987 */ 988 public EntryIDSet evaluateSubstringFilter(SearchFilter filter, 989 StringBuilder debugBuffer) 990 { 991 SubstringMatchingRule matchRule = 992 filter.getAttributeType().getSubstringMatchingRule(); 993 994 try 995 { 996 ArrayList<ByteString> elements = new ArrayList<ByteString>(); 997 EntryIDSet results = new EntryIDSet(); 998 999 if (filter.getSubInitialElement() != null) 1000 { 1001 // Use the equality index for initial substrings if possible. 1002 if (equalityIndex != null) 1003 { 1004 ByteString normValue = 1005 matchRule.normalizeSubstring(filter.getSubInitialElement()); 1006 byte[] normBytes = normValue.value(); 1007 1008 EntryIDSet list = matchInitialSubstring(normBytes); 1009 results.retainAll(list); 1010 1011 if (results.isDefined() && 1012 results.size() <= IndexFilter.FILTER_CANDIDATE_THRESHOLD) 1013 { 1014 1015 if(debugBuffer != null) 1016 { 1017 debugBuffer.append("[INDEX:"); 1018 debugBuffer.append(indexConfig.getAttribute(). 1019 getNameOrOID()); 1020 debugBuffer.append("."); 1021 debugBuffer.append("equality]"); 1022 } 1023 1024 return results; 1025 } 1026 } 1027 else 1028 { 1029 elements.add(filter.getSubInitialElement()); 1030 } 1031 } 1032 1033 if (substringIndex == null) 1034 { 1035 return results; 1036 } 1037 1038 // We do not distinguish between sub and final elements 1039 // in the substring index. Put all the elements into a single list. 1040 elements.addAll(filter.getSubAnyElements()); 1041 if (filter.getSubFinalElement() != null) 1042 { 1043 elements.add(filter.getSubFinalElement()); 1044 } 1045 1046 // Iterate through each substring element. 1047 for (ByteString element : elements) 1048 { 1049 // Normalize the substring according to the substring matching rule. 1050 ByteString normValue = matchRule.normalizeSubstring(element); 1051 byte[] normBytes = normValue.value(); 1052 1053 // Get the candidate entry IDs from the index. 1054 EntryIDSet list = matchSubstring(normBytes); 1055 1056 // Incorporate them into the results. 1057 results.retainAll(list); 1058 1059 // We may have reached the point of diminishing returns where 1060 // it is quicker to stop now and process the current small number of 1061 // candidates. 1062 if (results.isDefined() && 1063 results.size() <= IndexFilter.FILTER_CANDIDATE_THRESHOLD) 1064 { 1065 break; 1066 } 1067 } 1068 1069 if(debugBuffer != null) 1070 { 1071 debugBuffer.append("[INDEX:"); 1072 debugBuffer.append(indexConfig.getAttribute().getNameOrOID()); 1073 debugBuffer.append("."); 1074 debugBuffer.append("substring]"); 1075 } 1076 1077 return results; 1078 } 1079 catch (DirectoryException e) 1080 { 1081 if (debugEnabled()) 1082 { 1083 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1084 } 1085 return new EntryIDSet(); 1086 } 1087 } 1088 1089 /** 1090 * Retrieve the entry IDs that might have a value greater than or 1091 * equal to the lower bound value, and less than or equal to the 1092 * upper bound value. 1093 * 1094 * @param lowerValue The lower bound value 1095 * @param upperValue The upper bound value 1096 * @return The candidate entry IDs. 1097 */ 1098 public EntryIDSet evaluateBoundedRange(AttributeValue lowerValue, 1099 AttributeValue upperValue) 1100 { 1101 if (orderingIndex == null) 1102 { 1103 return new EntryIDSet(); 1104 } 1105 1106 try 1107 { 1108 // Use the ordering matching rule to normalize the values. 1109 OrderingMatchingRule orderingRule = 1110 getAttributeType().getOrderingMatchingRule(); 1111 1112 // Set the lower bound for a range search. 1113 byte[] lower = orderingRule.normalizeValue(lowerValue.getValue()).value(); 1114 1115 // Set the upper bound for a range search. 1116 byte[] upper = orderingRule.normalizeValue(upperValue.getValue()).value(); 1117 1118 // Read the range: lower <= keys <= upper. 1119 return orderingIndex.readRange(lower, upper, true, true); 1120 } 1121 catch (DirectoryException e) 1122 { 1123 if (debugEnabled()) 1124 { 1125 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1126 } 1127 return new EntryIDSet(); 1128 } 1129 } 1130 1131 /** 1132 * The default lexicographic byte array comparator. 1133 * Is there one available in the Java platform? 1134 */ 1135 static public class KeyComparator implements Comparator<byte[]> 1136 { 1137 /** 1138 * Compares its two arguments for order. Returns a negative integer, 1139 * zero, or a positive integer as the first argument is less than, equal 1140 * to, or greater than the second. 1141 * 1142 * @param a the first object to be compared. 1143 * @param b the second object to be compared. 1144 * @return a negative integer, zero, or a positive integer as the 1145 * first argument is less than, equal to, or greater than the 1146 * second. 1147 */ 1148 public int compare(byte[] a, byte[] b) 1149 { 1150 int i; 1151 for (i = 0; i < a.length && i < b.length; i++) 1152 { 1153 if (a[i] > b[i]) 1154 { 1155 return 1; 1156 } 1157 else if (a[i] < b[i]) 1158 { 1159 return -1; 1160 } 1161 } 1162 if (a.length == b.length) 1163 { 1164 return 0; 1165 } 1166 if (a.length > b.length) 1167 { 1168 return 1; 1169 } 1170 else 1171 { 1172 return -1; 1173 } 1174 } 1175 } 1176 1177 /** 1178 * Retrieve the entry IDs that might match an approximate filter. 1179 * 1180 * @param approximateFilter The approximate filter. 1181 * @param debugBuffer If not null, a diagnostic string will be written 1182 * which will help determine how the indexes contributed 1183 * to this search. 1184 * @return The candidate entry IDs that might contain the filter 1185 * assertion value. 1186 */ 1187 public EntryIDSet evaluateApproximateFilter(SearchFilter approximateFilter, 1188 StringBuilder debugBuffer) 1189 { 1190 if (approximateIndex == null) 1191 { 1192 return new EntryIDSet(); 1193 } 1194 1195 try 1196 { 1197 ApproximateMatchingRule approximateMatchingRule = 1198 approximateFilter.getAttributeType().getApproximateMatchingRule(); 1199 // Make a key from the normalized assertion value. 1200 byte[] keyBytes = 1201 approximateMatchingRule.normalizeValue( 1202 approximateFilter.getAssertionValue().getValue()).value(); 1203 DatabaseEntry key = new DatabaseEntry(keyBytes); 1204 1205 if(debugBuffer != null) 1206 { 1207 debugBuffer.append("[INDEX:"); 1208 debugBuffer.append(indexConfig.getAttribute().getNameOrOID()); 1209 debugBuffer.append("."); 1210 debugBuffer.append("approximate]"); 1211 } 1212 1213 // Read the key. 1214 return approximateIndex.readKey(key, null, LockMode.DEFAULT); 1215 } 1216 catch (DirectoryException e) 1217 { 1218 if (debugEnabled()) 1219 { 1220 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1221 } 1222 return new EntryIDSet(); 1223 } 1224 } 1225 1226 /** 1227 * Return the number of values that have exceeded the entry limit since this 1228 * object was created. 1229 * 1230 * @return The number of values that have exceeded the entry limit. 1231 */ 1232 public long getEntryLimitExceededCount() 1233 { 1234 long entryLimitExceededCount = 0; 1235 1236 if (equalityIndex != null) 1237 { 1238 entryLimitExceededCount += equalityIndex.getEntryLimitExceededCount(); 1239 } 1240 1241 if (presenceIndex != null) 1242 { 1243 entryLimitExceededCount += presenceIndex.getEntryLimitExceededCount(); 1244 } 1245 1246 if (substringIndex != null) 1247 { 1248 entryLimitExceededCount += substringIndex.getEntryLimitExceededCount(); 1249 } 1250 1251 if (orderingIndex != null) 1252 { 1253 entryLimitExceededCount += orderingIndex.getEntryLimitExceededCount(); 1254 } 1255 1256 if (approximateIndex != null) 1257 { 1258 entryLimitExceededCount += 1259 approximateIndex.getEntryLimitExceededCount(); 1260 } 1261 1262 return entryLimitExceededCount; 1263 } 1264 1265 /** 1266 * Get a list of the databases opened by this attribute index. 1267 * @param dbList A list of database containers. 1268 */ 1269 public void listDatabases(List<DatabaseContainer> dbList) 1270 { 1271 if (equalityIndex != null) 1272 { 1273 dbList.add(equalityIndex); 1274 } 1275 1276 if (presenceIndex != null) 1277 { 1278 dbList.add(presenceIndex); 1279 } 1280 1281 if (substringIndex != null) 1282 { 1283 dbList.add(substringIndex); 1284 } 1285 1286 if (orderingIndex != null) 1287 { 1288 dbList.add(orderingIndex); 1289 } 1290 1291 if (approximateIndex != null) 1292 { 1293 dbList.add(approximateIndex); 1294 } 1295 } 1296 1297 /** 1298 * Get a string representation of this object. 1299 * @return return A string representation of this object. 1300 */ 1301 public String toString() 1302 { 1303 return getName(); 1304 } 1305 1306 /** 1307 * {@inheritDoc} 1308 */ 1309 public synchronized boolean isConfigurationChangeAcceptable( 1310 LocalDBIndexCfg cfg, 1311 List<Message> unacceptableReasons) 1312 { 1313 AttributeType attrType = cfg.getAttribute(); 1314 1315 if (cfg.getIndexType().contains(LocalDBIndexCfgDefn.IndexType.EQUALITY)) 1316 { 1317 if (equalityIndex == null && attrType.getEqualityMatchingRule() == null) 1318 { 1319 Message message = ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get( 1320 String.valueOf(String.valueOf(attrType)), "equality"); 1321 unacceptableReasons.add(message); 1322 return false; 1323 } 1324 } 1325 1326 if (cfg.getIndexType().contains(LocalDBIndexCfgDefn.IndexType.SUBSTRING)) 1327 { 1328 if (substringIndex == null && attrType.getSubstringMatchingRule() == null) 1329 { 1330 Message message = ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get( 1331 String.valueOf(attrType), "substring"); 1332 unacceptableReasons.add(message); 1333 return false; 1334 } 1335 1336 } 1337 1338 if (cfg.getIndexType().contains(LocalDBIndexCfgDefn.IndexType.ORDERING)) 1339 { 1340 if (orderingIndex == null && attrType.getOrderingMatchingRule() == null) 1341 { 1342 Message message = ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get( 1343 String.valueOf(attrType), "ordering"); 1344 unacceptableReasons.add(message); 1345 return false; 1346 } 1347 } 1348 if (cfg.getIndexType().contains(LocalDBIndexCfgDefn.IndexType.APPROXIMATE)) 1349 { 1350 if (approximateIndex == null && 1351 attrType.getApproximateMatchingRule() == null) 1352 { 1353 Message message = ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get( 1354 String.valueOf(attrType), "approximate"); 1355 unacceptableReasons.add(message); 1356 return false; 1357 } 1358 } 1359 1360 return true; 1361 } 1362 1363 /** 1364 * {@inheritDoc} 1365 */ 1366 public synchronized ConfigChangeResult applyConfigurationChange( 1367 LocalDBIndexCfg cfg) 1368 { 1369 ConfigChangeResult ccr; 1370 boolean adminActionRequired = false; 1371 ArrayList<Message> messages = new ArrayList<Message>(); 1372 try 1373 { 1374 AttributeType attrType = cfg.getAttribute(); 1375 String name = 1376 entryContainer.getDatabasePrefix() + "_" + attrType.getNameOrOID(); 1377 int indexEntryLimit = cfg.getIndexEntryLimit(); 1378 1379 if (cfg.getIndexType().contains(LocalDBIndexCfgDefn.IndexType.EQUALITY)) 1380 { 1381 if (equalityIndex == null) 1382 { 1383 // Adding equality index 1384 Indexer equalityIndexer = new EqualityIndexer(attrType); 1385 equalityIndex = new Index(name + ".equality", 1386 equalityIndexer, 1387 state, 1388 indexEntryLimit, 1389 cursorEntryLimit, 1390 false, 1391 env, 1392 entryContainer); 1393 equalityIndex.open(); 1394 1395 adminActionRequired = true; 1396 messages.add(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get( 1397 name + ".equality")); 1398 1399 } 1400 else 1401 { 1402 // already exists. Just update index entry limit. 1403 if(this.equalityIndex.setIndexEntryLimit(indexEntryLimit)) 1404 { 1405 1406 adminActionRequired = true; 1407 Message message = 1408 NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get( 1409 name + ".equality"); 1410 messages.add(message); 1411 this.equalityIndex.setIndexEntryLimit(indexEntryLimit); 1412 } 1413 } 1414 } 1415 else 1416 { 1417 if (equalityIndex != null) 1418 { 1419 entryContainer.exclusiveLock.lock(); 1420 try 1421 { 1422 entryContainer.deleteDatabase(equalityIndex); 1423 equalityIndex = null; 1424 } 1425 catch(DatabaseException de) 1426 { 1427 messages.add(Message.raw( 1428 StaticUtils.stackTraceToSingleLineString(de))); 1429 ccr = new ConfigChangeResult( 1430 DirectoryServer.getServerErrorResultCode(), false, messages); 1431 return ccr; 1432 } 1433 finally 1434 { 1435 entryContainer.exclusiveLock.unlock(); 1436 } 1437 } 1438 } 1439 1440 if (cfg.getIndexType().contains(LocalDBIndexCfgDefn.IndexType.PRESENCE)) 1441 { 1442 if(presenceIndex == null) 1443 { 1444 Indexer presenceIndexer = new PresenceIndexer(attrType); 1445 presenceIndex = new Index(name + ".presence", 1446 presenceIndexer, 1447 state, 1448 indexEntryLimit, 1449 cursorEntryLimit, 1450 false, 1451 env, 1452 entryContainer); 1453 presenceIndex.open(); 1454 1455 adminActionRequired = true; 1456 1457 messages.add(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get( 1458 name + ".presence")); 1459 } 1460 else 1461 { 1462 // already exists. Just update index entry limit. 1463 if(this.presenceIndex.setIndexEntryLimit(indexEntryLimit)) 1464 { 1465 adminActionRequired = true; 1466 1467 Message message = 1468 NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get( 1469 name + ".presence"); 1470 messages.add(message); 1471 } 1472 } 1473 } 1474 else 1475 { 1476 if (presenceIndex != null) 1477 { 1478 entryContainer.exclusiveLock.lock(); 1479 try 1480 { 1481 entryContainer.deleteDatabase(presenceIndex); 1482 presenceIndex = null; 1483 } 1484 catch(DatabaseException de) 1485 { 1486 messages.add(Message.raw( 1487 StaticUtils.stackTraceToSingleLineString(de))); 1488 ccr = new ConfigChangeResult( 1489 DirectoryServer.getServerErrorResultCode(), false, messages); 1490 return ccr; 1491 } 1492 finally 1493 { 1494 entryContainer.exclusiveLock.unlock(); 1495 } 1496 } 1497 } 1498 1499 if (cfg.getIndexType().contains(LocalDBIndexCfgDefn.IndexType.SUBSTRING)) 1500 { 1501 if(substringIndex == null) 1502 { 1503 Indexer substringIndexer = new SubstringIndexer( 1504 attrType, cfg.getSubstringLength()); 1505 substringIndex = new Index(name + ".substring", 1506 substringIndexer, 1507 state, 1508 indexEntryLimit, 1509 cursorEntryLimit, 1510 false, 1511 env, 1512 entryContainer); 1513 substringIndex.open(); 1514 1515 adminActionRequired = true; 1516 messages.add(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get( 1517 name + ".substring")); 1518 } 1519 else 1520 { 1521 // already exists. Just update index entry limit. 1522 if(this.substringIndex.setIndexEntryLimit(indexEntryLimit)) 1523 { 1524 adminActionRequired = true; 1525 Message message = 1526 NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get( 1527 name + ".substring"); 1528 messages.add(message); 1529 } 1530 1531 if(indexConfig.getSubstringLength() != 1532 cfg.getSubstringLength()) 1533 { 1534 Indexer substringIndexer = new SubstringIndexer( 1535 attrType, cfg.getSubstringLength()); 1536 this.substringIndex.setIndexer(substringIndexer); 1537 } 1538 } 1539 } 1540 else 1541 { 1542 if (substringIndex != null) 1543 { 1544 entryContainer.exclusiveLock.lock(); 1545 try 1546 { 1547 entryContainer.deleteDatabase(substringIndex); 1548 substringIndex = null; 1549 } 1550 catch(DatabaseException de) 1551 { 1552 messages.add(Message.raw( 1553 StaticUtils.stackTraceToSingleLineString(de))); 1554 ccr = new ConfigChangeResult( 1555 DirectoryServer.getServerErrorResultCode(), false, messages); 1556 return ccr; 1557 } 1558 finally 1559 { 1560 entryContainer.exclusiveLock.unlock(); 1561 } 1562 } 1563 } 1564 1565 if (cfg.getIndexType().contains(LocalDBIndexCfgDefn.IndexType.ORDERING)) 1566 { 1567 if(orderingIndex == null) 1568 { 1569 Indexer orderingIndexer = new OrderingIndexer(attrType); 1570 orderingIndex = new Index(name + ".ordering", 1571 orderingIndexer, 1572 state, 1573 indexEntryLimit, 1574 cursorEntryLimit, 1575 false, 1576 env, 1577 entryContainer); 1578 orderingIndex.open(); 1579 1580 adminActionRequired = true; 1581 messages.add(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get( 1582 name + ".ordering")); 1583 } 1584 else 1585 { 1586 // already exists. Just update index entry limit. 1587 if(this.orderingIndex.setIndexEntryLimit(indexEntryLimit)) 1588 { 1589 adminActionRequired = true; 1590 1591 Message message = 1592 NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get( 1593 name + ".ordering"); 1594 messages.add(message); 1595 } 1596 } 1597 } 1598 else 1599 { 1600 if (orderingIndex != null) 1601 { 1602 entryContainer.exclusiveLock.lock(); 1603 try 1604 { 1605 entryContainer.deleteDatabase(orderingIndex); 1606 orderingIndex = null; 1607 } 1608 catch(DatabaseException de) 1609 { 1610 messages.add(Message.raw( 1611 StaticUtils.stackTraceToSingleLineString(de))); 1612 ccr = new ConfigChangeResult( 1613 DirectoryServer.getServerErrorResultCode(), false, messages); 1614 return ccr; 1615 } 1616 finally 1617 { 1618 entryContainer.exclusiveLock.unlock(); 1619 } 1620 } 1621 } 1622 1623 if (cfg.getIndexType().contains( 1624 LocalDBIndexCfgDefn.IndexType.APPROXIMATE)) 1625 { 1626 if(approximateIndex == null) 1627 { 1628 Indexer approximateIndexer = new ApproximateIndexer(attrType); 1629 approximateIndex = new Index(name + ".approximate", 1630 approximateIndexer, 1631 state, 1632 indexEntryLimit, 1633 cursorEntryLimit, 1634 false, 1635 env, 1636 entryContainer); 1637 approximateIndex.open(); 1638 1639 adminActionRequired = true; 1640 1641 messages.add(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get( 1642 name + ".approximate")); 1643 } 1644 else 1645 { 1646 // already exists. Just update index entry limit. 1647 if(this.approximateIndex.setIndexEntryLimit(indexEntryLimit)) 1648 { 1649 adminActionRequired = true; 1650 1651 Message message = 1652 NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get( 1653 name + ".approximate"); 1654 messages.add(message); 1655 } 1656 } 1657 } 1658 else 1659 { 1660 if (approximateIndex != null) 1661 { 1662 entryContainer.exclusiveLock.lock(); 1663 try 1664 { 1665 entryContainer.deleteDatabase(approximateIndex); 1666 approximateIndex = null; 1667 } 1668 catch(DatabaseException de) 1669 { 1670 messages.add( 1671 Message.raw(StaticUtils.stackTraceToSingleLineString(de))); 1672 ccr = new ConfigChangeResult( 1673 DirectoryServer.getServerErrorResultCode(), false, messages); 1674 return ccr; 1675 } 1676 finally 1677 { 1678 entryContainer.exclusiveLock.unlock(); 1679 } 1680 } 1681 } 1682 1683 indexConfig = cfg; 1684 1685 return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired, 1686 messages); 1687 } 1688 catch(Exception e) 1689 { 1690 messages.add(Message.raw(StaticUtils.stackTraceToSingleLineString(e))); 1691 ccr = new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(), 1692 adminActionRequired, 1693 messages); 1694 return ccr; 1695 } 1696 } 1697 1698 /** 1699 * Set the index truststate. 1700 * @param txn A database transaction, or null if none is required. 1701 * @param trusted True if this index should be trusted or false 1702 * otherwise. 1703 * @throws DatabaseException If an error occurs in the JE database. 1704 */ 1705 public synchronized void setTrusted(Transaction txn, boolean trusted) 1706 throws DatabaseException 1707 { 1708 if (equalityIndex != null) 1709 { 1710 equalityIndex.setTrusted(txn, trusted); 1711 } 1712 1713 if (presenceIndex != null) 1714 { 1715 presenceIndex.setTrusted(txn, trusted); 1716 } 1717 1718 if (substringIndex != null) 1719 { 1720 substringIndex.setTrusted(txn, trusted); 1721 } 1722 1723 if (orderingIndex != null) 1724 { 1725 orderingIndex.setTrusted(txn, trusted); 1726 } 1727 1728 if (approximateIndex != null) 1729 { 1730 approximateIndex.setTrusted(txn, trusted); 1731 } 1732 } 1733 1734 /** 1735 * Set the rebuild status of this index. 1736 * @param rebuildRunning True if a rebuild process on this index 1737 * is running or False otherwise. 1738 */ 1739 public synchronized void setRebuildStatus(boolean rebuildRunning) 1740 { 1741 if (equalityIndex != null) 1742 { 1743 equalityIndex.setRebuildStatus(rebuildRunning); 1744 } 1745 1746 if (presenceIndex != null) 1747 { 1748 presenceIndex.setRebuildStatus(rebuildRunning); 1749 } 1750 1751 if (substringIndex != null) 1752 { 1753 substringIndex.setRebuildStatus(rebuildRunning); 1754 } 1755 1756 if (orderingIndex != null) 1757 { 1758 orderingIndex.setRebuildStatus(rebuildRunning); 1759 } 1760 1761 if (approximateIndex != null) 1762 { 1763 approximateIndex.setRebuildStatus(rebuildRunning); 1764 } 1765 } 1766 1767 /** 1768 * Get the JE database name prefix for indexes in this attribute 1769 * index. 1770 * 1771 * @return JE database name for this database container. 1772 */ 1773 public String getName() 1774 { 1775 StringBuilder builder = new StringBuilder(); 1776 builder.append(entryContainer.getDatabasePrefix()); 1777 builder.append("_"); 1778 builder.append(indexConfig.getAttribute().getNameOrOID()); 1779 return builder.toString(); 1780 } 1781 1782 /** 1783 * Return the equality index. 1784 * 1785 * @return The equality index. 1786 */ 1787 public Index getEqualityIndex() { 1788 return equalityIndex; 1789 } 1790 1791 /** 1792 * Return the approximate index. 1793 * 1794 * @return The approximate index. 1795 */ 1796 public Index getApproximateIndex() { 1797 return approximateIndex; 1798 } 1799 1800 /** 1801 * Return the ordering index. 1802 * 1803 * @return The ordering index. 1804 */ 1805 public Index getOrderingIndex() { 1806 return orderingIndex; 1807 } 1808 1809 /** 1810 * Return the substring index. 1811 * 1812 * @return The substring index. 1813 */ 1814 public Index getSubstringIndex() { 1815 return substringIndex; 1816 } 1817 1818 /** 1819 * Return the presence index. 1820 * 1821 * @return The presence index. 1822 */ 1823 public Index getPresenceIndex() { 1824 return presenceIndex; 1825 } 1826 1827 /** 1828 * Retrieves all the indexes used by this attribute index. 1829 * 1830 * @return A collection of all indexes in use by this attribute 1831 * index. 1832 */ 1833 public Collection<Index> getAllIndexes() { 1834 LinkedHashSet<Index> indexes = new LinkedHashSet<Index>(); 1835 1836 if (equalityIndex != null) 1837 { 1838 indexes.add(equalityIndex); 1839 } 1840 1841 if (presenceIndex != null) 1842 { 1843 indexes.add(presenceIndex); 1844 } 1845 1846 if (substringIndex != null) 1847 { 1848 indexes.add(substringIndex); 1849 } 1850 1851 if (orderingIndex != null) 1852 { 1853 indexes.add(orderingIndex); 1854 } 1855 1856 if (approximateIndex != null) 1857 { 1858 indexes.add(approximateIndex); 1859 } 1860 1861 return indexes; 1862 } 1863 }