/*
* 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
* 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
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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-2008 Sun Microsystems, Inc.
* Portions Copyright 2011 ForgeRock AS
*/
/**
* This class represents a VLV index. Each database record is a sorted list
* of entry IDs followed by sets of attribute values used to sort the entries.
* The entire set of entry IDs are broken up into sorted subsets to decrease
* the number of database retrievals needed for a range lookup. The records are
* keyed by the last entry's first sort attribute value. The list of entries
* in a particular database record maintains the property where the first sort
* attribute value is bigger then the previous key but smaller or equal
* to its own key.
*/
implements ConfigurationChangeListener<LocalDBVLVIndexCfg>
{
/**
* The tracer object for the debug logger.
*/
/**
* The comparator for vlvIndex keys.
*/
/**
* The limit on the number of entry IDs that may be indexed by one key.
*/
/**
* The cached count of entries in this index.
*/
/**
* A flag to indicate if this vlvIndex should be trusted to be consistent
* with the entries database.
*/
private boolean trusted = false;
/**
* A flag to indicate if a rebuild process is running on this vlvIndex.
*/
private boolean rebuildRunning = false;
/**
* The VLV vlvIndex configuration.
*/
/**
* The SortOrder in use by this VLV index to sort the entries.
*/
/**
* Create a new VLV vlvIndex object.
*
* @param config The VLV index config object to use for this VLV
* index.
* @param state The state database to persist vlvIndex state info.
* @param env The JE Environemnt
* @param entryContainer The database entryContainer holding this vlvIndex.
* @throws com.sleepycat.je.DatabaseException
* If an error occurs in the JE database.
* @throws ConfigException if a error occurs while reading the VLV index
* configuration
*/
throws DatabaseException, ConfigException
{
try
{
this.filter =
}
catch(Exception e)
{
throw new ConfigException(msg);
}
{
try
{
{
ascending[i] = false;
}
else
{
ascending[i] = true;
{
}
}
}
catch(Exception e)
{
throw new ConfigException(msg);
}
{
throw new ConfigException(msg);
}
}
{
dbNodupsConfig.setReadOnly(true);
dbNodupsConfig.setAllowCreate(false);
dbNodupsConfig.setTransactional(false);
}
{
dbNodupsConfig.setAllowCreate(true);
dbNodupsConfig.setTransactional(false);
dbNodupsConfig.setDeferredWrite(true);
}
else
{
dbNodupsConfig.setAllowCreate(true);
dbNodupsConfig.setTransactional(true);
}
this.dbConfig = dbNodupsConfig;
this.dbConfig.setOverrideBtreeComparator(true);
{
// If there are no entries in the entry container then there
// is no reason why this vlvIndex can't be upgraded to trusted.
setTrusted(null, true);
}
this.config.addChangeListener(this);
}
/**
* {@inheritDoc}
*/
{
super.open();
try
{
{
}
}
finally
{
}
}
/**
* Close the VLV index.
*
* @throws DatabaseException if a JE database error occurs while
* closing the index.
*/
{
super.close();
this.config.removeChangeListener(this);
}
/**
* Update the vlvIndex for a new entry.
*
* @param txn A database transaction, or null if none is required.
* @param entryID The entry ID.
* @param entry The entry to be indexed.
* @return True if the entry ID for the entry are added. False if
* the entry ID already exists.
* @throws DatabaseException If an error occurs in the JE database.
* @throws org.opends.server.types.DirectoryException If a Directory Server
* error occurs.
* @throws JebException If an error occurs in the JE backend.
*/
{
{
}
return false;
}
/**
* Update the vlvIndex for a new entry.
*
* @param buffer The index buffer to buffer the changes.
* @param entryID The entry ID.
* @param entry The entry to be indexed.
* @return True if the entry ID for the entry are added. False if
* the entry ID already exists.
* @throws DirectoryException If a Directory Server
* error occurs.
*/
throws DirectoryException
{
{
buffer.getVLVIndex(this);
if(bufferedValues == null)
{
}
{
return true;
}
if(bufferedAddedValues == null)
{
}
return true;
}
return false;
}
/**
* Update the vlvIndex for a deleted entry.
*
* @param txn The database transaction to be used for the deletions
* @param entryID The entry ID
* @param entry The contents of the deleted entry.
* @return True if the entry was successfully removed from this VLV index
* or False otherwise.
* @throws DatabaseException If an error occurs in the JE database.
* @throws DirectoryException If a Directory Server error occurs.
* @throws JebException If an error occurs in the JE backend.
*/
{
{
}
return false;
}
/**
* Update the vlvIndex for a deleted entry.
*
* @param buffer The database transaction to be used for the deletions
* @param entryID The entry ID
* @param entry The contents of the deleted entry.
* @return True if the entry was successfully removed from this VLV index
* or False otherwise.
* @throws DirectoryException If a Directory Server error occurs.
*/
throws DirectoryException
{
{
buffer.getVLVIndex(this);
if(bufferedValues == null)
{
}
{
return true;
}
if(bufferedDeletedValues == null)
{
}
return true;
}
return false;
}
/**
* Update the vlvIndex to reflect a sequence of modifications in a Modify
* operation.
*
* @param txn The JE transaction to use for database updates.
* @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.
* @return True if the modification was successfully processed or False
* otherwise.
* @throws JebException If an error occurs during an operation on a
* JE database.
* @throws DatabaseException If an error occurs during an operation on a
* JE database.
* @throws DirectoryException If a Directory Server error occurs.
*/
{
{
{
// The entry should still be indexed. See if any sorted attributes are
// changed.
boolean sortAttributeModified = false;
{
{
{
sortAttributeModified = true;
break;
}
{
{
sortAttributeModified = true;
break;
}
}
}
{
break;
}
}
{
boolean success;
// Sorted attributes have changed. Reindex the entry;
return success;
}
}
else
{
// The modifications caused the new entry to be unindexed. Remove from
// vlvIndex.
}
}
else
{
{
// The modifications caused the new entry to be indexed. Add to
// vlvIndex.
}
}
// The modifications does not affect this vlvIndex
return true;
}
/**
* Update the vlvIndex to reflect a sequence of modifications in a Modify
* operation.
*
* @param buffer The database transaction to be used for the deletions
* @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.
* @return True if the modification was successfully processed or False
* otherwise.
* @throws DatabaseException If an error occurs during an operation on a
* JE database.
* @throws DirectoryException If a Directory Server error occurs.
*/
throws DatabaseException, DirectoryException
{
{
{
// The entry should still be indexed. See if any sorted attributes are
// changed.
boolean sortAttributeModified = false;
{
{
{
sortAttributeModified = true;
break;
}
{
{
sortAttributeModified = true;
break;
}
}
}
{
break;
}
}
{
boolean success;
// Sorted attributes have changed. Reindex the entry;
return success;
}
}
else
{
// The modifications caused the new entry to be unindexed. Remove from
// vlvIndex.
}
}
else
{
{
// The modifications caused the new entry to be indexed. Add to
// vlvIndex.
}
}
// The modifications does not affect this vlvIndex
return true;
}
/**
* Put a sort values set in this VLV index.
*
* @param txn The transaction to use when retriving the set or NULL if it is
* not required.
* @param sortValuesSet The SortValuesSet to put.
* @return True if the sortValuesSet was put successfully or False otherwise.
* @throws JebException If an error occurs during an operation on a
* JE database.
* @throws DatabaseException If an error occurs during an operation on a
* JE database.
* @throws DirectoryException If a Directory Server error occurs.
*/
{
}
/**
* Get a sorted values set that should contain the entry with the given
* information.
*
* @param txn The transaction to use when retriving the set or NULL if it is
* not required.
* @param entryID The entry ID to use.
* @param values The values to use.
* @return The SortValuesSet that should contain the entry with the given
* information.
* @throws DatabaseException If an error occurs during an operation on a
* JE database.
* @throws DirectoryException If a Directory Server error occurs.
*/
throws DatabaseException, DirectoryException
{
try
{
{
// There are no records in the database
if(debugEnabled())
{
}
sortValuesSet = new SortValuesSet(this);
}
else
{
if(debugEnabled())
{
"%s\nSearch Key:%s\nFound Key:%s\n",
}
this);
}
}
finally
{
}
return sortValuesSet;
}
/**
* Search for entries matching the entry ID and attribute values and
* return its entry ID.
*
* @param txn The JE transaction to use for database updates.
* @param entryID The entry ID to search for.
* @param values The values to search for.
* @return The index of the entry ID matching the values or -1 if its not
* found.
* @throws DatabaseException If an error occurs during an operation on a
* JE database.
* @throws JebException If an error occurs during an operation on a
* JE database.
* @throws DirectoryException If a Directory Server error occurs.
*/
{
if(pos < 0)
{
return false;
}
return true;
}
{
boolean success = true;
try
{
}
finally
{
}
{
// There are no records in the database
if(debugEnabled())
{
}
sortValuesSet = new SortValuesSet(this);
}
else
{
if(debugEnabled())
{
"%s\nSearch Key:%s\nFound Key:%s\n",
}
this);
}
if(newSize >= sortedSetCapacity)
{
if(debugEnabled())
{
" the entry size of %d. Spliting into two sets with " +
}
}
else
{
// TODO: What about phantoms?
}
if(success)
{
}
return success;
}
{
try
{
}
finally
{
}
{
if(debugEnabled())
{
"%s\nSearch Key:%s\nFound Key:%s\n",
}
this);
{
}
else
{
}
if(success)
{
}
return success;
}
else
{
return false;
}
}
/**
* Update the vlvIndex with the specified values to add and delete.
*
* @param txn A database transaction, or null if none is required.
* @param addedValues The values to add to the VLV index.
* @param deletedValues The values to delete from the VLV index.
* @throws DatabaseException If an error occurs in the JE database.
* @throws DirectoryException If a Directory Server
* error occurs.
*/
throws DirectoryException, DatabaseException
{
// Handle cases where nothing is changed early to avoid
// DB access.
{
return;
}
if(addedValues != null)
{
}
if(deletedValues != null)
{
}
while(true)
{
{
{
// Start from the smallest values from either set.
{
}
else
{
}
}
else
{
}
}
{
}
else
{
break;
}
try
{
}
finally
{
}
{
// There are no records in the database
if(debugEnabled())
{
}
sortValuesSet = new SortValuesSet(this);
}
else
{
if(debugEnabled())
{
"%s\nSearch Key:%s\nFound Key:%s\n",
}
this);
}
{
// This is the last unbounded set.
{
{
}
else
{
}
}
{
{
}
else
{
}
}
}
else
{
{
{
}
else
{
}
}
{
{
}
else
{
}
}
}
if(newSize >= sortedSetCapacity)
{
if(debugEnabled())
{
" the entry size of %d. Spliting into two sets with " +
}
}
else if(newSize == 0)
{
}
else
{
}
}
}
/**
* Evaluate a search with sort control using this VLV index.
*
* @param txn The transaction to used when reading the index or NULL if it is
* not required.
* @param searchOperation The search operation to evaluate.
* @param sortControl The sort request control to evaluate.
* @param vlvRequest The VLV request control to evaluate or NULL if VLV is not
* requested.
* @param debugBuilder If not null, a diagnostic string will be written
* which will help determine how this index contributed
* to this search.
* @return The sorted EntryIDSet containing the entry IDs that match the
* search criteria.
* @throws DirectoryException If a Directory Server error occurs.
* @throws DatabaseException If an error occurs in the JE database.
*/
throws DirectoryException, DatabaseException
{
if(!trusted || rebuildRunning)
{
return null;
}
{
return null;
}
{
return null;
}
{
return null;
}
{
return null;
}
if (debugBuilder != null)
{
""));
}
long[] selectedIDs = new long[0];
if(vlvRequest != null)
{
{
if (targetOffset < 0)
{
// The client specified a negative target offset. This should never
// be allowed.
message);
}
else if (targetOffset == 0)
{
// This is an easy mistake to make, since VLV offsets start at 1
// instead of 0. We'll assume the client meant to use 1.
targetOffset = 1;
}
if (startPos < 0)
{
// This can happen if beforeCount >= offset, and in this case we'll
// just adjust the start position to ignore the range of beforeCount
// that doesn't exist.
startPos = 0;
}
else if(startPos >= currentCount)
{
// The start position is beyond the end of the list. In this case,
// we'll assume that the start position was one greater than the
// size of the list and will only return the beforeCount entries.
// The start position is beyond the end of the list. In this case,
// we'll assume that the start position was one greater than the
// size of the list and will only return the beforeCount entries.
afterCount = 0;
}
selectedIDs = new long[count];
try
{
//Locate the set that contains the target entry.
int cursorCount = 0;
int selectedPos = 0;
{
if(debugEnabled())
{
4);
4);
"vlvIndex %s\nSearch Key:%s\nFound Key:%s\n",
}
i++, selectedPos++)
{
}
}
if (selectedPos < count)
{
// We don't have enough entries in the set to meet the requested
// page size, so we'll need to shorten the array.
long[] newIDArray = new long[selectedPos];
}
if(debugBuilder != null)
{
}
}
finally
{
}
}
else
{
int targetOffset = 0;
int includedBeforeCount = 0;
int includedAfterCount = 0;
try
{
{
if(debugEnabled())
{
4);
4);
"vlvIndex %s\nSearch Key:%s\nFound Key:%s\n",
}
assertionValue[0] =
int adjustedTargetOffset =
if(adjustedTargetOffset < 0)
{
// For a negative return value r, the vlvIndex -(r+1) gives the
// array index of the ID that is greater then the assertion value.
}
// Iterate through all the sort values sets before this one to find
// the target offset in the index.
while(true)
{
for(int i = lastOffset;
{
}
{
break;
}
{
lastIDs =
}
else
{
}
}
// Set the cursor back to the position of the target entry set
// Add the target and after count entries if the target was found.
int afterIDCount = 0;
while(true)
{
for(int i = lastOffset;
i++)
{
}
{
break;
}
{
break;
}
lastIDs =
lastOffset = 0;
}
{
}
if(debugBuilder != null)
{
}
}
}
finally
{
}
}
}
else
{
int currentCount = 0;
try
{
{
if(debugEnabled())
{
"%s\nSearch Key:%s\nFound Key:%s\n",
}
}
}
finally
{
}
selectedIDs = new long[currentCount];
int pos = 0;
{
}
if(debugBuilder != null)
{
}
}
}
/**
* Set the vlvIndex trust state.
* @param txn A database transaction, or null if none is required.
* @param trusted True if this vlvIndex should be trusted or false
* otherwise.
* @throws DatabaseException If an error occurs in the JE database.
*/
throws DatabaseException
{
}
/**
* Return true iff this index is trusted.
* @return the trusted state of this index
*/
public boolean isTrusted()
{
return trusted;
}
/**
* Set the rebuild status of this vlvIndex.
* @param rebuildRunning True if a rebuild process on this vlvIndex
* is running or False otherwise.
*/
{
this.rebuildRunning = rebuildRunning;
}
/**
* Gets the values to sort on from the entry.
*
* @param entry The entry to get the values from.
* @return The attribute values to sort on.
*/
{
{
{
// There may be multiple versions of this attribute in the target entry
// (e.g., with different sets of options), and it may also be a
// multivalued attribute. In that case, we need to find the value that
// is the best match for the corresponding sort key (i.e., for sorting
// in ascending order, we want to find the lowest value; for sorting in
// descending order, we want to find the highest value). This is
// handled by the SortKey.compareValues method.
{
for (AttributeValue v : a)
{
{
sortValue = v;
}
{
sortValue = v;
}
}
}
}
}
return values;
}
/**
* Encode a VLV database key with the given information.
*
* @param entryID The entry ID to encode.
* @param values The values to encode.
* @return The encoded bytes.
* @throws DirectoryException If a Directory Server error occurs.
*/
throws DirectoryException
{
for (AttributeValue v : values)
{
byte[] vBytes;
if(v == null)
{
}
else
{
}
}
return builder.getBackingArray();
}
/**
* Decode a VLV database key.
*
* @param keyBytes The byte array to decode.
* @return The sort values represented by the key bytes.
* @throws DirectoryException If a Directory Server error occurs.
*/
throws DirectoryException
{
{
return null;
}
int vBytesPos = 0;
{
{
int valueLengthBytes = valueLength;
valueLength = 0;
{
}
}
if(valueLength == 0)
{
attributeValues[i] = null;
}
else
{
byte[] valueBytes = new byte[valueLength];
attributeValues[i] =
}
vBytesPos += valueLength;
}
// FIXME: Should pos+offset method for decoding IDs be added to
// JebFormat?
long v = 0;
{
v <<= 8;
v |= (keyBytes[i] & 0xFF);
}
}
/**
* Get the sorted set capacity configured for this VLV index.
*
* @return The sorted set capacity.
*/
public int getSortedSetCapacity()
{
return sortedSetCapacity;
}
/**
* Indicates if the given entry should belong in this VLV index.
*
* @param entry The entry to check.
* @return True if the given entry should belong in this VLV index or False
* otherwise.
* @throws DirectoryException If a Directory Server error occurs.
*/
{
{
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
public synchronized boolean isConfigurationChangeAcceptable(
{
try
{
this.filter =
}
catch(Exception e)
{
return false;
}
{
try
{
{
ascending[i] = false;
}
else
{
ascending[i] = true;
{
}
}
}
catch(Exception e)
{
return false;
}
{
return false;
}
}
return true;
}
/**
* {@inheritDoc}
*/
{
boolean adminActionRequired = false;
// Update base DN only if changed..
{
adminActionRequired = true;
}
// Update scope only if changed.
{
adminActionRequired = true;
}
// Update sort set capacity only if changed.
if(config.getMaxBlockSize() !=
{
// Require admin action only if the new capacity is larger. Otherwise,
// we will lazyly update the sorted sets.
if(config.getMaxBlockSize() <
{
adminActionRequired = true;
}
}
// Update the filter only if changed.
{
try
{
this.filter =
adminActionRequired = true;
}
catch(Exception e)
{
{
}
}
}
// Update the sort order only if changed.
cfg.getSortOrder()))
{
{
try
{
{
ascending[i] = false;
}
else
{
ascending[i] = true;
{
}
}
}
catch(Exception e)
{
{
}
}
{
{
}
}
else
{
}
}
// We have to close the database and open it using the new comparator.
try
{
this.close();
this.open();
}
catch(DatabaseException de)
{
{
}
}
finally
{
}
adminActionRequired = true;
}
{
trusted = false;
try
{
}
catch(DatabaseException de)
{
{
}
}
}
}
}