AttributeIndex.java revision 8ac57ee1cd50fcc3d02b36bea4ab1335924f1d7a
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2006-2010 Sun Microsystems, Inc.
* Portions Copyright 2011-2015 ForgeRock AS
* Portions Copyright 2014 Manuel Gaupp
*/
/**
* Class representing an attribute index.
* We have a separate database for each type of indexing, which makes it easy
* to tell which attribute indexes are configured. The different types of
* indexing are equality, presence, substrings and ordering. The keys in the
* ordering index are ordered by setting the btree comparator to the ordering
* matching rule comparator.
* Note that the values in the equality index are normalized by the equality
* matching rule, whereas the values in the ordering index are normalized
* by the ordering matching rule. If these could be guaranteed to be identical
* then we would not need a separate ordering index.
*/
public class AttributeIndex
{
/** Type of the index filter. */
static enum IndexFilterType
{
/** Equality. */
/** Presence. */
/** Ordering. */
/** Ordering. */
/** Substring. */
/** Approximate. */
{
}
/** {@inheritDoc} */
{
}
}
/*
* FIXME Matthew Swift: Once the matching rules have been migrated we should
* revisit this class. All of the evaluateXXX methods should go (the Matcher
* class in the SDK could implement the logic, I hope).
*/
/** The entryContainer in which this attribute index resides. */
private final EntryContainer entryContainer;
/** The attribute index configuration. */
private LocalDBIndexCfg indexConfig;
private IndexingOptions indexingOptions;
/** The mapping from names to indexes. */
/**
* Create a new attribute index object.
*
* @param indexConfig The attribute index configuration.
* @param entryContainer The entryContainer of this attribute index.
* @throws ConfigException if a configuration related error occurs.
*/
public AttributeIndex(LocalDBIndexCfg indexConfig, EntryContainer entryContainer) throws ConfigException
{
this.entryContainer = entryContainer;
this.indexConfig = indexConfig;
this.indexIdToIndexes = Collections.unmodifiableMap(buildIndexes(entryContainer, indexConfig, indexingOptions));
this.indexQueryFactory = new IndexQueryFactoryImpl(indexIdToIndexes, indexingOptions, indexConfig.getAttribute());
}
{
switch (indexType)
{
case PRESENCE:
break;
case EXTENSIBLE:
indexers = getExtensibleIndexers(config.getAttribute(), config.getIndexExtensibleMatchingRule(), options);
break;
case APPROXIMATE:
indexers =
break;
case EQUALITY:
indexers =
break;
case ORDERING:
indexers =
break;
case SUBSTRING:
indexers =
break;
default:
throw new ConfigException(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attributeType, indexType.toString()));
}
buildAndRegisterIndexesWithIndexers(entryContainer, attributeType, indexEntryLimit, indexers, indexes);
}
return indexes;
}
{
}
private static MatchingRule throwIfNoMatchingRule(AttributeType attributeType, IndexType type, MatchingRule rule)
throws ConfigException
{
{
throw new ConfigException(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attributeType, type.toString()));
}
return rule;
}
int indexEntryLimit,
{
{
{
}
}
}
private static Collection<Indexer> getExtensibleIndexers(AttributeType attributeType, Set<String> extensibleRules,
{
{
throw new ConfigException(
}
{
{
continue;
}
}
return indexers;
}
{
switch (indexType)
{
case APPROXIMATE:
return attrType.getApproximateMatchingRule();
case EQUALITY:
return attrType.getEqualityMatchingRule();
case ORDERING:
return attrType.getOrderingMatchingRule();
case SUBSTRING:
return attrType.getSubstringMatchingRule();
default:
}
}
{
}
private static String getIndexName(EntryContainer entryContainer, AttributeType attrType, String indexID)
{
}
/**
* Open the attribute index.
*
* @throws DatabaseException if a JE database error occurs while
* opening the index.
*/
public void open() throws DatabaseException
{
{
}
indexConfig.addChangeListener(this);
}
/** Closes the attribute index. */
public void close()
{
indexConfig.removeChangeListener(this);
// The entryContainer is responsible for closing the JE databases.
}
/**
* Get the attribute type of this attribute index.
* @return The attribute type of this attribute index.
*/
public AttributeType getAttributeType()
{
return indexConfig.getAttribute();
}
/**
* Return the indexing options of this AttributeIndex.
*
* @return the indexing options of this AttributeIndex.
*/
public IndexingOptions getIndexingOptions()
{
return indexQueryFactory.getIndexingOptions();
}
/**
* Get the JE index configuration used by this index.
* @return The configuration in effect.
*/
public LocalDBIndexCfg getConfiguration()
{
return indexConfig;
}
/**
* Update the attribute index for a new entry.
*
* @param buffer The index buffer to use to store the added keys
* @param entryID The entry ID.
* @param entry The contents of the new entry.
* @throws DatabaseException If an error occurs in the JE database.
* @throws DirectoryException If a Directory Server error occurs.
*/
public void addEntry(IndexBuffer buffer, EntryID entryID, Entry entry) throws DatabaseException, DirectoryException
{
{
}
}
/**
* Update the attribute index for a deleted entry.
*
* @param buffer The index buffer to use to store the deleted keys
* @param entryID The entry ID
* @param entry The contents of the deleted entry.
* @throws DatabaseException If an error occurs in the JE database.
* @throws DirectoryException If a Directory Server error occurs.
*/
throws DatabaseException, DirectoryException
{
{
}
}
/**
* Update the index to reflect a sequence of modifications in a Modify
* operation.
*
* @param buffer The index buffer used to buffer up the index changes.
* @param entryID The ID of the entry that was modified.
* @param oldEntry The entry before the modifications were applied.
* @param newEntry The entry after the modifications were applied.
* @param mods The sequence of modifications in the Modify operation.
* @throws DatabaseException If an error occurs during an operation on a
* JE database.
*/
throws DatabaseException
{
{
}
}
/**
* Makes a byte string representing a substring index key for
* one substring of a value.
*
* @param bytes The byte array containing the value.
* @param pos The starting position of the substring.
* @param len The length of the substring.
* @return A byte string containing a substring key.
*/
{
}
/**
* Decompose an attribute value into a set of substring index keys.
* The ID of the entry containing this value should be inserted
* into the list of each of these keys.
*
* @param value A byte array containing the normalized attribute value.
* @return A set of index keys.
*/
{ // FIXME replace this code with SDK's
// AbstractSubstringMatchingRuleImpl.SubstringIndexer.createKeys()
// Eliminate duplicates by putting the keys into a set.
// Sorting the keys will ensure database record locks are acquired
// in a consistent order and help prevent transaction deadlocks between
// concurrent writers.
// Example: The value is ABCDE and the substring length is 3.
// We produce the keys ABC BCD CDE DE E
// To find values containing a short substring such as DE,
// iterate through keys with prefix DE. To find values
// containing a longer substring such as BCDE, read keys BCD and CDE.
{
}
return set;
}
/**
* Retrieve the entry IDs that might match the provided assertion.
*
* @param indexQuery
* The query used to retrieve entries.
* @param indexName
* The name of index used to retrieve entries.
* @param filter
* The filter on entries.
* @param debugBuffer
* If not null, a diagnostic string will be written which will help
* determine how the indexes contributed to this search.
* @param monitor
* The database environment monitor provider that will keep index
* filter usage statistics.
* @return The candidate entry IDs that might contain the filter assertion
* value.
*/
{
LocalizableMessageBuilder debugMessage = monitor.isFilterUseEnabled() ? new LocalizableMessageBuilder() : null;
if (debugBuffer != null)
{
}
if (monitor.isFilterUseEnabled())
{
{
}
else
{
}
}
return results;
}
/**
* Retrieve the entry IDs that might match two filters that restrict a value
* to both a lower bound and an upper bound.
*
* @param filter1
* The first filter, that is either a less-or-equal filter or a
* greater-or-equal filter.
* @param filter2
* The second filter, that is either a less-or-equal filter or a
* greater-or-equal filter. It must not be of the same type than the
* first filter.
* @param debugBuffer
* If not null, a diagnostic string will be written which will help
* determine how the indexes contributed to this search.
* @param monitor
* The database environment monitor provider that will keep index
* filter usage statistics.
* @return The candidate entry IDs that might contain match both filters.
*/
public EntryIDSet evaluateBoundedRange(SearchFilter filter1, SearchFilter filter2, StringBuilder debugBuffer,
{
// TODO : this implementation is not optimal
// as it implies two separate evaluations instead of a single one,
// thus defeating the purpose of the optimization done
// in IndexFilter#evaluateLogicalAndFilter method.
// One solution could be to implement a boundedRangeAssertion that combine
// the two operations in one.
return results;
}
private EntryIDSet evaluate(SearchFilter filter, StringBuilder debugBuffer, DatabaseEnvironmentMonitor monitor)
{
IndexFilterType indexFilterType = isLessOrEqual ? IndexFilterType.LESS_OR_EQUAL : IndexFilterType.GREATER_OR_EQUAL;
}
/**
* Retrieve the entry IDs that might match a filter.
*
* @param indexFilterType the index type filter
* @param filter The filter.
* @param debugBuffer If not null, a diagnostic string will be written
* which will help determine how the indexes contributed
* to this search.
* @param monitor The database environment monitor provider that will keep
* index filter usage statistics.
* @return The candidate entry IDs that might contain a value
* that matches the filter type.
*/
public EntryIDSet evaluateFilter(IndexFilterType indexFilterType, SearchFilter filter, StringBuilder debugBuffer,
{
try
{
}
catch (DecodeException e)
{
logger.traceException(e);
return new EntryIDSet();
}
}
private IndexQuery getIndexQuery(IndexFilterType indexFilterType, SearchFilter filter) throws DecodeException
{
switch (indexFilterType)
{
case EQUALITY:
case PRESENCE:
return indexQueryFactory.createMatchAllQuery();
case GREATER_OR_EQUAL:
case LESS_OR_EQUAL:
case SUBSTRING:
case APPROXIMATE:
default:
return null;
}
}
/**
* Delegator to {@link ByteSequence#BYTE_ARRAY_COMPARATOR}.
* <p>
* This intermediate class is necessary to satisfy JE's requirements for a btree comparator.
*
* @see com.sleepycat.je.DatabaseConfig#setBtreeComparator(Comparator)
*/
public static class KeyComparator implements Comparator<byte[]>
{
/** The instance. */
/** {@inheritDoc} */
public int compare(byte[] a, byte[] b)
{
}
}
/**
* Return the number of values that have exceeded the entry limit since this
* object was created.
*
* @return The number of values that have exceeded the entry limit.
*/
public long getEntryLimitExceededCount()
{
long entryLimitExceededCount = 0;
{
}
return entryLimitExceededCount;
}
/**
* Get a list of the databases opened by this attribute index.
* @param dbList A list of database containers.
*/
{
}
/**
* Get a string representation of this object.
* @return return A string representation of this object.
*/
{
return getName();
}
/** {@inheritDoc} */
public synchronized boolean isConfigurationChangeAcceptable(
{
{
return false;
}
{
{
return false;
}
}
return true;
}
{
{
unacceptableReasons.add(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attrType, indexType.toString()));
return false;
}
return true;
}
/** {@inheritDoc} */
public synchronized ConfigChangeResult applyConfigurationChange(final LocalDBIndexCfg newConfiguration)
{
final IndexingOptions newIndexingOptions = new JEIndexingOptions(newConfiguration.getSubstringLength());
try
{
Map<String, Index> newIndexIdToIndexes = buildIndexes(entryContainer, newConfiguration, newIndexingOptions);
// Replace instances of Index created by buildIndexes() with the one already opened and present in the actual
// indexIdToIndexes
// Open added indexes *before* adding them to indexIdToIndexes
{
}
indexQueryFactory = new IndexQueryFactoryImpl(indexIdToIndexes, indexingOptions, indexConfig.getAttribute());
// FIXME: There is no guarantee here that deleted index are not currently involved in a query
{
}
{
}
}
catch (Exception e)
{
}
return ccr;
}
{
{
ccr.setAdminActionRequired(true);
}
}
{
{
// This index can still be used since index size limit doesn't impact validity of the results.
ccr.setAdminActionRequired(true);
}
}
{
try
{
}
finally
{
}
}
/**
* Return true iff this index is trusted.
* @return the trusted state of this index
*/
public boolean isTrusted()
{
{
{
return false;
}
}
return true;
}
/**
* Get the JE database name prefix for indexes in this attribute index.
*
* @return JE database name for this database container.
*/
{
return entryContainer.getDatabasePrefix()
+ "_"
}
}
/**
* Retrieves all the indexes used by this attribute index.
*
* @return An immutable collection of all indexes in use by this attribute
* index.
*/
return indexIdToIndexes.values();
}
/**
* Retrieve the entry IDs that might match an extensible filter.
*
* @param filter The extensible filter.
* @param debugBuffer If not null, a diagnostic string will be written
* which will help determine how the indexes contributed
* to this search.
* @param monitor The database environment monitor provider that will keep
* index filter usage statistics.
* @return The candidate entry IDs that might contain the filter
* assertion value.
*/
{
//Get the Matching Rule OID of the filter.
/*
* Use the default equality index in two conditions:
* 1. There is no matching rule provided
* 2. The matching rule specified is actually the default equality.
*/
if (matchRuleOID == null
{
//No matching rule is defined; use the default equality matching rule.
}
if (!ruleHasAtLeasOneIndex(rule))
{
if (monitor.isFilterUseEnabled())
{
}
}
try
{
if (debugBuffer != null)
{
{
.append(".")
}
}
final IndexQuery indexQuery = rule.getAssertion(filter.getAssertionValue()).createIndexQuery(indexQueryFactory);
LocalizableMessageBuilder debugMessage = monitor.isFilterUseEnabled() ? new LocalizableMessageBuilder() : null;
if (monitor.isFilterUseEnabled())
{
{
}
else
{
}
}
return results;
}
catch (DecodeException e)
{
logger.traceException(e);
}
}
{
{
{
return true;
}
}
return false;
}
/** This class extends the IndexConfig for JE Backend. */
private static final class JEIndexingOptions implements IndexingOptions
{
/** The length of the substring index. */
private int substringLength;
/**
* Creates a new JEIndexConfig instance.
* @param substringLength The length of the substring.
*/
private JEIndexingOptions(int substringLength)
{
this.substringLength = substringLength;
}
/** {@inheritDoc} */
public int substringKeySize()
{
return substringLength;
}
}
}