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 }