mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

boli
31.19.2007 e25d897908d93ab4f9cabadb1029ad3352602d5d
This set of fixes mainly address the issues where the JE backend does not handle attributes with options and subtypes correctly when they are being indexed. 
With this fix:
- All values of an indexed attribute type will be indexed correctly on modifies, adds, and deletes.
- Updates to subordinate types will now update the superior type if its indexed.
- Adding and deleting superior attribute types that are not allowed by any object classes (ie. name) will be correctly handled
- Deleting all values from an attribute with no options will no longer delete the values from the same attribute but with options.

12 files modified
768 ■■■■■ changed files
opends/src/server/org/opends/server/backends/jeb/ApproximateIndexer.java 32 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/EntryContainer.java 14 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/EqualityIndexer.java 145 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/OrderingIndexer.java 89 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/PresenceIndexer.java 19 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/SubstringIndexer.java 55 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/VLVIndex.java 15 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/Entry.java 185 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/resource/config-changes.ldif 12 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java 154 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/types/EntrySchemaCheckingTestCase.java 30 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java 18 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/ApproximateIndexer.java
@@ -176,7 +176,37 @@
                          Set<ASN1OctetString> addKeys,
                          Set<ASN1OctetString> delKeys)
  {
    replaceEntry(txn, oldEntry, newEntry, addKeys, delKeys);
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    HashSet<AttributeValue> newValues;
    HashSet<AttributeValue> oldValues;
    if(newAttributes == null)
    {
      indexAttribute(oldAttributes, delKeys);
    }
    else
    {
      if(oldAttributes == null)
      {
        indexAttribute(newAttributes, addKeys);
      }
      else
      {
        HashSet<ASN1OctetString> newKeys =
            new HashSet<ASN1OctetString>();
        HashSet<ASN1OctetString> oldKeys =
            new HashSet<ASN1OctetString>();
        indexAttribute(newAttributes, newKeys);
        indexAttribute(oldAttributes, oldKeys);
        addKeys.addAll(newKeys);
        addKeys.removeAll(oldKeys);
        delKeys.addAll(oldKeys);
        delKeys.removeAll(newKeys);
      }
    }
  }
  /**
opends/src/server/org/opends/server/backends/jeb/EntryContainer.java
@@ -3789,15 +3789,27 @@
    {
      // Check whether any modifications apply to this indexed attribute.
      boolean attributeModified = false;
      AttributeType indexAttributeType = index.getAttributeType();
      Iterable<AttributeType> subTypes =
          DirectoryServer.getSchema().getSubTypes(indexAttributeType);
      for (Modification mod : mods)
      {
        Attribute modAttr = mod.getAttribute();
        AttributeType modAttrType = modAttr.getAttributeType();
        if (modAttrType.equals(index.getAttributeType()))
        if (modAttrType.equals(indexAttributeType))
        {
          attributeModified = true;
          break;
        }
        for(AttributeType subType : subTypes)
        {
          if(modAttrType.equals(subType))
          {
            attributeModified = true;
            break;
          }
        }
      }
      if (attributeModified)
      {
opends/src/server/org/opends/server/backends/jeb/EqualityIndexer.java
@@ -45,7 +45,6 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.ArrayList;
/**
 * An implementation of an Indexer for attribute equality.
@@ -191,140 +190,44 @@
                          Set<ASN1OctetString> delKeys)
       throws DatabaseException
  {
    // Optimize for the case where there are no attribute options
    // involved and only simple addition and deletion of individual values.
    // An issue with attribute options is that the values can not be assumed
    // to be unique.
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    HashSet<AttributeValue> newValues;
    HashSet<AttributeValue> oldValues;
    List<Attribute> beforeList;
    List<Attribute> afterList;
    beforeList = oldEntry.getAttribute(attributeType);
    afterList = newEntry.getAttribute(attributeType);
    boolean hasOptions = false;
    if (beforeList != null)
    if(newAttributes == null)
    {
      for (Attribute a : beforeList)
      {
        if (a.hasOptions())
        {
          hasOptions = true;
          break;
        }
      }
    }
    if (afterList != null)
    {
      for (Attribute a : afterList)
      {
        if (a.hasOptions())
        {
          hasOptions = true;
          break;
        }
      }
    }
    boolean hasOnlySimpleMods = true;
    List<Modification> simpleMods = new ArrayList<Modification>();
    for (Modification mod : mods)
    {
      Attribute modAttr = mod.getAttribute();
      AttributeType modAttrType = modAttr.getAttributeType();
      if (modAttrType.equals(attributeType))
      {
        if (modAttr.hasOptions())
        {
          hasOptions = true;
        }
        switch (mod.getModificationType())
        {
          case ADD:
            simpleMods.add(mod);
            break;
          case DELETE:
            if (!modAttr.hasValue())
            {
              hasOnlySimpleMods = false;
      indexAttribute(oldAttributes, delKeys);
            }
            else
            {
              simpleMods.add(mod);
            }
            break;
          default:
            hasOnlySimpleMods = false;
            break;
        }
      }
    }
    if (hasOnlySimpleMods && !hasOptions)
      if(oldAttributes == null)
    {
      // This is the optimized case where there are no attribute options
      // involved and only simple addition and deletion of individual values.
      // It should be efficient for adding and/or deleting a few values of an
      // attribute with lots of values.
      for (Modification mod : simpleMods)
      {
        Set<AttributeValue> values = mod.getAttribute().getValues();
        Set<ASN1OctetString> keys =
             new HashSet<ASN1OctetString>(values.size());
        indexValues(values, keys);
        switch (mod.getModificationType())
        {
          case ADD:
            for (ASN1OctetString key : keys)
            {
              if (delKeys.contains(key))
              {
                delKeys.remove(key);
        indexAttribute(newAttributes, addKeys);
              }
              else
              {
                addKeys.add(key);
              }
            }
            break;
          case DELETE:
            for (ASN1OctetString key : keys)
        newValues = new HashSet<AttributeValue>();
        oldValues = new HashSet<AttributeValue>();
        for(Attribute a : newAttributes)
            {
              if (addKeys.contains(key))
          newValues.addAll(a.getValues());
        }
        for(Attribute a : oldAttributes)
              {
                addKeys.remove(key);
          oldValues.addAll(a.getValues());
              }
              else
              {
                delKeys.add(key);
              }
            }
            break;
        }
      }
    }
    else
    {
      // This is the non-optimized case that should be correct in all cases.
      Set<ASN1OctetString> oldSet = new HashSet<ASN1OctetString>();
      indexAttribute(beforeList, oldSet);
        HashSet<AttributeValue> valuesToAdd =
            new HashSet<AttributeValue>(newValues);
        HashSet<AttributeValue> valuesToDel =
            new HashSet<AttributeValue>(oldValues);
        valuesToAdd.removeAll(oldValues);
        valuesToDel.removeAll(newValues);
      Set<ASN1OctetString> newSet = new HashSet<ASN1OctetString>();
      indexAttribute(afterList, newSet);
      HashSet<ASN1OctetString> removeSet = new HashSet<ASN1OctetString>(oldSet);
      removeSet.removeAll(newSet);
      delKeys.addAll(removeSet);
      HashSet<ASN1OctetString> addSet = new HashSet<ASN1OctetString>(newSet);
      addSet.removeAll(oldSet);
      addKeys.addAll(addSet);
        indexValues(valuesToDel, delKeys);
        indexValues(valuesToAdd, addKeys);
      }
    }
  }
opends/src/server/org/opends/server/backends/jeb/OrderingIndexer.java
@@ -180,72 +180,43 @@
                          Set<ASN1OctetString> delKeys)
       throws DatabaseException
  {
    List<Attribute> beforeList;
    beforeList = oldEntry.getAttribute(attributeType);
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    HashSet<AttributeValue> newValues;
    HashSet<AttributeValue> oldValues;
    // Pick out the modifications that apply to this indexed attribute
    /**
     * FIXME unusual modifications can insert spurious index values
     * The following sequence of modifications will insert A into the
     * index, yet A is not a resulting value for the attribute.
     *
     * add: cn
     * cn: A
     * -
     * replace: cn
     * cn: B
     * -
     *
     */
    for (Modification mod : mods)
    if(newAttributes == null)
    {
      Attribute modAttr = mod.getAttribute();
      AttributeType modAttrType = modAttr.getAttributeType();
      if (modAttrType.equals(attributeType))
      {
        switch (mod.getModificationType())
        {
          case REPLACE:
          case INCREMENT:
            if (beforeList != null)
            {
              for (Attribute attr : beforeList)
              {
                if (attr.hasOptions(modAttr.getOptions()))
                {
                  indexValues(attr.getValues(), delKeys);
                }
              }
            }
            indexValues(modAttr.getValues(), addKeys);
            break;
          case ADD:
            indexValues(modAttr.getValues(), addKeys);
            break;
          case DELETE:
            if (!modAttr.hasValue())
            {
              if (beforeList != null)
              {
                for (Attribute attr : beforeList)
                {
                  if (attr.hasOptions(modAttr.getOptions()))
                  {
                    indexValues(attr.getValues(), delKeys);
                  }
                }
              }
      indexAttribute(oldAttributes, delKeys);
            }
            else
            {
              indexValues(modAttr.getValues(), delKeys);
      if(oldAttributes == null)
      {
        indexAttribute(newAttributes, addKeys);
            }
            break;
      else
      {
        newValues = new HashSet<AttributeValue>();
        oldValues = new HashSet<AttributeValue>();
        for(Attribute a : newAttributes)
        {
          newValues.addAll(a.getValues());
        }
        for(Attribute a : oldAttributes)
        {
          oldValues.addAll(a.getValues());
        }
        HashSet<AttributeValue> valuesToAdd =
            new HashSet<AttributeValue>(newValues);
        HashSet<AttributeValue> valuesToDel =
            new HashSet<AttributeValue>(oldValues);
        valuesToAdd.removeAll(oldValues);
        valuesToDel.removeAll(newValues);
        indexValues(valuesToDel, delKeys);
        indexValues(valuesToAdd, addKeys);
      }
    }
  }
opends/src/server/org/opends/server/backends/jeb/PresenceIndexer.java
@@ -171,6 +171,23 @@
                          Set<ASN1OctetString> delKeys)
       throws DatabaseException
  {
    replaceEntry(txn, oldEntry, newEntry, addKeys, delKeys);
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    if(oldAttributes == null)
    {
      if(newAttributes != null)
      {
        addKeys.add(
              new ASN1OctetString(AttributeIndex.presenceKey.getData()));
      }
    }
    else
    {
      if(newAttributes == null)
      {
        delKeys.add(
              new ASN1OctetString(AttributeIndex.presenceKey.getData()));
      }
    }
  }
}
opends/src/server/org/opends/server/backends/jeb/SubstringIndexer.java
@@ -34,7 +34,6 @@
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@@ -179,7 +178,37 @@
                          Set<ASN1OctetString> addKeys,
                          Set<ASN1OctetString> delKeys)
  {
    replaceEntry(txn, oldEntry, newEntry, addKeys, delKeys);
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    HashSet<AttributeValue> newValues;
    HashSet<AttributeValue> oldValues;
    if(newAttributes == null)
    {
      indexAttribute(oldAttributes, delKeys);
    }
    else
    {
      if(oldAttributes == null)
      {
        indexAttribute(newAttributes, addKeys);
      }
      else
      {
        HashSet<ASN1OctetString> newKeys =
            new HashSet<ASN1OctetString>();
        HashSet<ASN1OctetString> oldKeys =
            new HashSet<ASN1OctetString>();
        indexAttribute(newAttributes, newKeys);
        indexAttribute(oldAttributes, oldKeys);
        addKeys.addAll(newKeys);
        addKeys.removeAll(oldKeys);
        delKeys.addAll(oldKeys);
        delKeys.removeAll(newKeys);
      }
    }
  }
@@ -187,23 +216,36 @@
  /**
   * Generate the set of substring index keys for an attribute.
   * @param attrList The attribute for which substring keys are required.
   * @param addKeys The set into which the generated keys will be inserted.
   * @param keys The set into which the generated keys will be inserted.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Set<ASN1OctetString> addKeys)
                              Set<ASN1OctetString> keys)
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      LinkedHashSet<AttributeValue> values = attr.getValues();
      indexValues(attr.getValues(), keys);
    }
  }
  /**
   * Generate the set of index keys for a set of attribute values.
   * @param values The set of attribute values to be indexed.
   * @param keys The set into which the keys will be inserted.
   */
  private void indexValues(Set<AttributeValue> values,
                           Set<ASN1OctetString> keys)
  {
    if (values == null) return;
      for (AttributeValue value : values)
      {
        try
        {
          byte[] normalizedBytes = value.getNormalizedValue().value();
          substringKeys(normalizedBytes, addKeys);
        substringKeys(normalizedBytes, keys);
        }
        catch (DirectoryException e)
        {
@@ -214,7 +256,6 @@
        }
      }
    }
  }
  /**
   * Decompose an attribute value into a set of substring index keys.
opends/src/server/org/opends/server/backends/jeb/VLVIndex.java
@@ -383,14 +383,25 @@
        SortKey[] sortKeys = sortOrder.getSortKeys();
        for(SortKey sortKey : sortKeys)
        {
          AttributeType attributeType = sortKey.getAttributeType();
          Iterable<AttributeType> subTypes =
              DirectoryServer.getSchema().getSubTypes(attributeType);
          for(Modification mod : mods)
          {
            if(mod.getAttribute().getAttributeType().
                equals(sortKey.getAttributeType()))
            AttributeType modAttrType = mod.getAttribute().getAttributeType();
            if(modAttrType.equals(attributeType))
            {
              sortAttributeModified = true;
              break;
            }
            for(AttributeType subType : subTypes)
            {
              if(modAttrType.equals(subType))
              {
                sortAttributeModified = true;
                break;
              }
            }
          }
          if(sortAttributeModified)
          {
opends/src/server/org/opends/server/types/Entry.java
@@ -608,25 +608,47 @@
  }
  /**
   * Indicates whether this entry contains the specified attribute.
   * Any subordinate attribute of the specified attribute will also
   * be used in the determination.
   *
   * @param  attributeType  The attribute type for which to make the
   *                        determination.
   *
   * @param  attributeType       The attribute type for which to
   *                             make the determination.
   *
   * @return  <CODE>true</CODE> if this entry contains the specified
   *          attribute, or <CODE>false</CODE> if not.
   */
  public boolean hasAttribute(AttributeType attributeType)
  {
    return hasAttribute(attributeType, true);
  }
  /**
   * Indicates whether this entry contains the specified attribute.
   *
   * @param  attributeType       The attribute type for which to
   *                             make the determination.
   * @param  includeSubordinates Whether to include any subordinate
   *                             attributes of the attribute type
   *                             being retrieved.
   *
   * @return  <CODE>true</CODE> if this entry contains the specified
   *          attribute, or <CODE>false</CODE> if not.
   */
  public boolean hasAttribute(AttributeType attributeType,
                              boolean includeSubordinates)
  {
    if (userAttributes.containsKey(attributeType) ||
        operationalAttributes.containsKey(attributeType))
    {
      return true;
    }
    if (attributeType.mayHaveSubordinateTypes())
    if (includeSubordinates &&
        attributeType.mayHaveSubordinateTypes())
    {
      for (AttributeType at : schema.getSubTypes(attributeType))
      {
@@ -643,13 +665,14 @@
  }
  /**
   * Indicates whether this entry contains the specified attribute
   * with all of the options in the provided set.
   *
   * @param  attributeType     The attribute type for which to make
   * with all of the options in the provided set. Any subordinate
   * attribute of the specified attribute will also be used in
   *                           the determination.
   *
   * @param  attributeType       The attribute type for which to
   *                             make the determination.
   * @param  attributeOptions  The set of options to use in the
   *                           determination.
   *
@@ -659,8 +682,32 @@
  public boolean hasAttribute(AttributeType attributeType,
                              Set<String> attributeOptions)
  {
    return hasAttribute(attributeType, true, attributeOptions);
  }
  /**
   * Indicates whether this entry contains the specified attribute
   * with all of the options in the provided set.
   *
   * @param  attributeType       The attribute type for which to
   *                             make the determination.
   * @param  includeSubordinates Whether to include any subordinate
   *                             attributes of the attribute type
   *                             being retrieved.
   * @param  attributeOptions    The set of options to use in the
   *                             determination.
   *
   * @return  <CODE>true</CODE> if this entry contains the specified
   *          attribute, or <CODE>false</CODE> if not.
   */
  public boolean hasAttribute(AttributeType attributeType,
                              boolean includeSubordinates,
                              Set<String> attributeOptions)
  {
    List<Attribute> attributes;
    if (attributeType.mayHaveSubordinateTypes())
    if (includeSubordinates &&
        attributeType.mayHaveSubordinateTypes())
    {
      attributes = new LinkedList<Attribute>();
      List<Attribute> attrs = userAttributes.get(attributeType);
@@ -726,13 +773,12 @@
    return false;
  }
  /**
   * Retrieves the requested attribute element(s) for the specified
   * attribute type.  The list returned may include multiple elements
   * if the same attribute exists in the entry multiple times with
   * different sets of options.
   * different sets of options. It may also include any subordinate
   * attributes of the attribute being retrieved.
   *
   * @param  attributeType  The attribute type to retrieve.
   *
@@ -742,7 +788,30 @@
   */
  public List<Attribute> getAttribute(AttributeType attributeType)
  {
    if (attributeType.mayHaveSubordinateTypes())
    return getAttribute(attributeType, true);
  }
  /**
   * Retrieves the requested attribute element(s) for the specified
   * attribute type.  The list returned may include multiple elements
   * if the same attribute exists in the entry multiple times with
   * different sets of options.
   *
   * @param  attributeType       The attribute type to retrieve.
   * @param  includeSubordinates Whether to include any subordinate
   *                             attributes of the attribute type
   *                             being retrieved.
   *
   * @return  The requested attribute element(s) for the specified
   *          attribute type, or <CODE>null</CODE> if the specified
   *          attribute type is not present in this entry.
   */
  public List<Attribute> getAttribute(AttributeType attributeType,
                                      boolean includeSubordinates)
  {
    if (includeSubordinates &&
        attributeType.mayHaveSubordinateTypes())
    {
      List<Attribute> attributes = new LinkedList<Attribute>();
@@ -821,7 +890,9 @@
   * Retrieves the requested attribute element(s) for the attribute
   * with the specified name or OID.  The list returned may include
   * multiple elements if the same attribute exists in the entry
   * multiple times with different sets of options.
   * multiple times with different sets of options. It may also
   * include any subordinate attributes of the attribute being
   * retrieved.
   * <BR><BR>
   * Note that this method should only be used in cases in which the
   * Directory Server schema has no reference of an attribute type
@@ -842,7 +913,7 @@
    {
      if (attr.hasNameOrOID(lowerName))
      {
        return getAttribute(attr);
        return getAttribute(attr, true);
      }
    }
@@ -850,7 +921,7 @@
    {
      if (attr.hasNameOrOID(lowerName))
      {
        return getAttribute(attr);
        return getAttribute(attr, true);
      }
    }
@@ -865,17 +936,16 @@
    return null;
  }
  /**
   * Retrieves the requested attribute element(s) for the specified
   * attribute type.  The list returned may include multiple elements
   * if the same attribute exists in the entry multiple times with
   * different sets of options.
   * different sets of options. It may also include any subordinate
   * attributes of the attribute being retrieved.
   *
   * @param  attributeType  The attribute type to retrieve.
   * @param  options        The set of attribute options to include in
   *                        matching elements.
   * @param  options             The set of attribute options to
   *                             include in matching elements.
   *
   * @return  The requested attribute element(s) for the specified
   *          attribute type, or <CODE>null</CODE> if the specified
@@ -885,8 +955,34 @@
  public List<Attribute> getAttribute(AttributeType attributeType,
                                      Set<String> options)
  {
    return getAttribute(attributeType, true, options);
  }
  /**
   * Retrieves the requested attribute element(s) for the specified
   * attribute type.  The list returned may include multiple elements
   * if the same attribute exists in the entry multiple times with
   * different sets of options.
   *
   * @param  attributeType       The attribute type to retrieve.
   * @param  includeSubordinates Whether to include any subordinate
   *                             attributes of the attribute type
   *                             being retrieved.
   * @param  options             The set of attribute options to
   *                             include in matching elements.
   *
   * @return  The requested attribute element(s) for the specified
   *          attribute type, or <CODE>null</CODE> if the specified
   *          attribute type is not present in this entry with the
   *          provided set of options.
   */
  public List<Attribute> getAttribute(AttributeType attributeType,
                                      boolean includeSubordinates,
                                      Set<String> options)
  {
    List<Attribute> attributes = new LinkedList<Attribute>();
    if (attributeType.mayHaveSubordinateTypes())
    if (includeSubordinates &&
        attributeType.mayHaveSubordinateTypes())
    {
      List<Attribute> attrs = userAttributes.get(attributeType);
      if (attrs != null)
@@ -1053,7 +1149,7 @@
  public final <T> T getAttributeValue(AttributeType attributeType,
      AttributeValueDecoder<T> decoder) throws DirectoryException
  {
    List<Attribute> attributes = getAttribute(attributeType);
    List<Attribute> attributes = getAttribute(attributeType, true);
    AttributeValueIterable values =
         new AttributeValueIterable(attributes);
    Iterator<AttributeValue> iterator = values.iterator();
@@ -1100,7 +1196,7 @@
      Collection<T> collection)
      throws DirectoryException
  {
    List<Attribute> attributes = getAttribute(attributeType);
    List<Attribute> attributes = getAttribute(attributeType, true);
    AttributeValueIterable values =
         new AttributeValueIterable(attributes);
@@ -1664,7 +1760,7 @@
    attachment = null;
    List<Attribute> attrList =
         getAttribute(attribute.getAttributeType());
         getAttribute(attribute.getAttributeType(), false);
    if (attrList == null)
    {
      // There are no instances of the specified attribute in this
@@ -1721,10 +1817,11 @@
  /**
   * Removes all instances of the specified attribute type from this
   * entry.  If the provided attribute type is the objectclass type,
   * then all objectclass values will be removed (but must be replaced
   * for the entry to be valid).  If the specified attribute type is
   * not present in this entry, then this method will have no effect.
   * entry, including any instances with options.  If the provided
   * attribute type is the objectclass type, then all objectclass
   * values will be removed (but must be replaced for the entry to
   * be valid).  If the specified attribute type is not present in
   * this entry, then this method will have no effect.
   *
   * @param  attributeType  The attribute type for the attribute to
   *                        remove from this entry.
@@ -1753,12 +1850,10 @@
  /**
   * Removes the attribute with the provided type and set of options
   * from this entry.  If the provided set of options is
   * <CODE>null</CODE> or empty, then all occurrences of the specified
   * attribute will be removed.  Otherwise, only the instance with the
   * exact set of options provided will be removed.  This has no
   * effect if the specified attribute is not present in this entry
   * with the given set of options.
   * from this entry.  Only the instance with the exact set of
   * options provided will be removed.  This has no effect if the
   * specified attribute is not present in this entry with the given
   * set of options.
   *
   * @param  attributeType  The attribute type for the attribute to
   *                        remove from this entry.
@@ -1774,12 +1869,6 @@
  {
    attachment = null;
    if ((options == null) || options.isEmpty())
    {
      return removeAttribute(attributeType);
    }
    else
    {
      List<Attribute> attrList = userAttributes.get(attributeType);
      if (attrList == null)
      {
@@ -1812,7 +1901,6 @@
      return removed;
    }
  }
@@ -1906,7 +1994,7 @@
      }
      List<Attribute> attrList =
           getAttribute(attribute.getAttributeType());
           getAttribute(attribute.getAttributeType(), false);
      if (attrList == null)
      {
        return false;
@@ -1944,11 +2032,11 @@
      LinkedHashSet<AttributeValue> valueSet = attribute.getValues();
      if ((valueSet == null) || valueSet.isEmpty())
      {
        return removeAttribute(attribute.getAttributeType());
        return removeAttribute(attribute.getAttributeType(), null);
      }
      List<Attribute> attrList =
           getAttribute(attribute.getAttributeType());
           getAttribute(attribute.getAttributeType(), false);
      if (attrList == null)
      {
        return false;
@@ -1971,7 +2059,8 @@
          if (existingValueSet.isEmpty())
          {
            return removeAttribute(attribute.getAttributeType());
            return removeAttribute(attribute.getAttributeType(),
                null);
          }
          return true;
@@ -2050,7 +2139,7 @@
  public boolean hasValue(AttributeType attributeType,
                          Set<String> options, AttributeValue value)
  {
    List<Attribute> attrList = getAttribute(attributeType);
    List<Attribute> attrList = getAttribute(attributeType, true);
    if ((attrList == null) || attrList.isEmpty())
    {
      return false;
@@ -2196,7 +2285,7 @@
        break;
      case INCREMENT:
        List<Attribute> attrList = getAttribute(t);
        List<Attribute> attrList = getAttribute(t, false);
        if ((attrList == null) || attrList.isEmpty())
        {
          Message message =
opends/tests/unit-tests-testng/resource/config-changes.ldif
@@ -1246,6 +1246,18 @@
objectClass: ds-cfg-branch
cn: Index
dn: ds-cfg-attribute=name,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config
changetype: add
objectClass: top
objectClass: ds-cfg-local-db-index
ds-cfg-attribute: name
ds-cfg-index-type: presence
ds-cfg-index-type: equality
ds-cfg-index-type: substring
ds-cfg-index-type: ordering
ds-cfg-index-type: approximate
ds-cfg-index-entry-limit: 4000
dn: ds-cfg-attribute=cn,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config
changetype: add
objectClass: top
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java
@@ -129,6 +129,9 @@
        "objectClass: organizationalPerson",
        "objectClass: inetOrgPerson",
        "givenName: Aaren",
        "givenName;lang-fr: test2",
        "givenName;lang-cn: test2",
        "givenName;lang-es: test3",
        "sn: Atp",
        "cn: Aaren Atp",
        "initials: APA",
@@ -1019,7 +1022,8 @@
    Entry newEntry;
    EntryID entryID;
    AttributeType attribute;
    AttributeIndex index;
    AttributeIndex titleIndex;
    AttributeIndex nameIndex;
    HashSet<ASN1OctetString> addKeys;
    DatabaseEntry key;
    PresenceIndexer presenceIndexer;
@@ -1036,12 +1040,46 @@
      ArrayList<Modification> modifications = new ArrayList<Modification>();
      modifications.add(new Modification(ModificationType.ADD, new
          Attribute("title", "debugger")));
      attribute = DirectoryServer.getAttributeType("title");
      LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
      values.add(new AttributeValue(attribute, "debugger2"));
      LinkedHashSet<String> options = new LinkedHashSet<String>(1);
      options.add("lang-en");
      Attribute attr = new Attribute(attribute, "title", options, values);
      modifications.add(new Modification(ModificationType.ADD, attr));
      modifications.add(new Modification(ModificationType.DELETE, new
          Attribute("cn", "Aaren Atp")));
      modifications.add(new Modification(ModificationType.ADD, new
          Attribute("cn", "Aaren Rigor")));
      modifications.add(new Modification(ModificationType.ADD, new
          Attribute("cn", "Aarenister Rigor")));
      attribute = DirectoryServer.getAttributeType("givenname");
      values = new LinkedHashSet<AttributeValue>();
      values.add(new AttributeValue(attribute, "test"));
      options = new LinkedHashSet<String>(1);
      options.add("lang-de");
      attr = new Attribute(attribute, "givenName", options, values);
      modifications.add(new Modification(ModificationType.ADD, attr));
      attribute = DirectoryServer.getAttributeType("givenname");
      values = new LinkedHashSet<AttributeValue>();
      values.add(new AttributeValue(attribute, "test2"));
      options = new LinkedHashSet<String>(1);
      options.add("lang-cn");
      attr = new Attribute(attribute, "givenName", options, values);
      modifications.add(new Modification(ModificationType.DELETE, attr));
      attribute = DirectoryServer.getAttributeType("givenname");
      values = new LinkedHashSet<AttributeValue>();
      values.add(new AttributeValue(attribute, "newtest3"));
      options = new LinkedHashSet<String>(1);
      options.add("lang-es");
      attr = new Attribute(attribute, "givenName", options, values);
      modifications.add(new Modification(ModificationType.REPLACE, attr));
      modifications.add(new Modification(ModificationType.REPLACE, new
          Attribute("employeenumber", "222")));
@@ -1053,19 +1091,31 @@
      assertNotNull(entryID);
      attribute = newEntry.getAttribute("title").get(0).getAttributeType();
      index = ec.getAttributeIndex(attribute);
      attribute = DirectoryServer.getAttributeType("title");
      titleIndex = ec.getAttributeIndex(attribute);
      attribute = DirectoryServer.getAttributeType("name");
      nameIndex = ec.getAttributeIndex(attribute);
      //This current entry in the DB shouldn't be in the presence index.
      //This current entry in the DB shouldn't be in the presence titleIndex.
      addKeys = new HashSet<ASN1OctetString>();
      addKeys.add(new ASN1OctetString(AttributeIndex.presenceKey.getData()));
      key = new DatabaseEntry();
      for (ASN1OctetString keyBytes : addKeys) {
        key.setData(keyBytes.value());
      }
      assertEquals(index.presenceIndex.containsID(null, key, entryID),
      assertEquals(titleIndex.presenceIndex.containsID(null, key, entryID),
          ConditionResult.FALSE);
      //This current entry should be in the presence nameIndex.
      addKeys = new HashSet<ASN1OctetString>();
      addKeys.add(new ASN1OctetString(AttributeIndex.presenceKey.getData()));
      key = new DatabaseEntry();
      for (ASN1OctetString keyBytes : addKeys) {
        key.setData(keyBytes.value());
      }
      assertEquals(nameIndex.presenceIndex.containsID(null, key, entryID),
          ConditionResult.TRUE);
      ArrayList<Control> noControls = new ArrayList<Control>(0);
      InternalClientConnection conn =
          InternalClientConnection.getRootConnection();
@@ -1098,56 +1148,124 @@
              entry.getAttribute("cn").get(0).getAttributeType(),
              "Aaren Atp")));
      options = new LinkedHashSet<String>();
      options.add("lang-de");
      assertTrue(entry.getAttribute("givenname", options).get(0).getValues().contains(
          new AttributeValue(
              entry.getAttribute("givenname", options).get(0).getAttributeType(),
              "test")));
      options = new LinkedHashSet<String>();
      options.add("lang-cn");
      assertNull
          (entry.getAttribute("givenname", options));
      options = new LinkedHashSet<String>();
      options.add("lang-es");
      assertTrue(entry.getAttribute("givenname", options).get(0).getValues().contains(
          new AttributeValue(
              entry.getAttribute("givenname", options).get(0).getAttributeType(),
              "newtest3")));
      options = new LinkedHashSet<String>();
      options.add("lang-fr");
      assertTrue(entry.getAttribute("givenname", options).get(0).getValues().contains(
          new AttributeValue(
              entry.getAttribute("givenname", options).get(0).getAttributeType(),
              "test2")));
      assertTrue(entry.getAttribute("employeenumber").contains(new
          Attribute("employeenumber", "222")));
      assertFalse(entry.getAttribute("employeenumber").contains(new
          Attribute("employeenumber", "1")));
      addKeys = new HashSet<ASN1OctetString>();
      presenceIndexer = new PresenceIndexer(index.getAttributeType());
      presenceIndexer = new PresenceIndexer(titleIndex.getAttributeType());
      presenceIndexer.indexEntry(null, entry, addKeys);
      key = new DatabaseEntry();
      for (ASN1OctetString keyBytes : addKeys) {
        key.setData(keyBytes.value());
      }
      assertEquals(index.presenceIndex.containsID(null, key, entryID),
        assertEquals(titleIndex.presenceIndex.containsID(null, key, entryID),
          ConditionResult.TRUE);
      }
      addKeys = new HashSet<ASN1OctetString>();
      orderingIndexer = new OrderingIndexer(index.getAttributeType());
      presenceIndexer = new PresenceIndexer(nameIndex.getAttributeType());
      presenceIndexer.indexEntry(null, entry, addKeys);
      key = new DatabaseEntry();
      for (ASN1OctetString keyBytes : addKeys) {
        key.setData(keyBytes.value());
        assertEquals(nameIndex.presenceIndex.containsID(null, key, entryID),
          ConditionResult.TRUE);
      }
      addKeys = new HashSet<ASN1OctetString>();
      orderingIndexer = new OrderingIndexer(titleIndex.getAttributeType());
      orderingIndexer.indexEntry(null, entry, addKeys);
      key = new DatabaseEntry();
      for (ASN1OctetString keyBytes : addKeys) {
        key.setData(keyBytes.value());
      }
      assertEquals(index.orderingIndex.containsID(null, key, entryID),
        assertEquals(titleIndex.orderingIndex.containsID(null, key, entryID),
          ConditionResult.TRUE);
      }
      addKeys = new HashSet<ASN1OctetString>();
      equalityIndexer = new EqualityIndexer(index.getAttributeType());
      orderingIndexer = new OrderingIndexer(nameIndex.getAttributeType());
      orderingIndexer.indexEntry(null, entry, addKeys);
      key = new DatabaseEntry();
      for (ASN1OctetString keyBytes : addKeys) {
        key.setData(keyBytes.value());
        assertEquals(nameIndex.orderingIndex.containsID(null, key, entryID),
          ConditionResult.TRUE);
      }
      addKeys = new HashSet<ASN1OctetString>();
      equalityIndexer = new EqualityIndexer(titleIndex.getAttributeType());
      equalityIndexer.indexEntry(null, entry, addKeys);
      key = new DatabaseEntry();
      for (ASN1OctetString keyBytes : addKeys) {
        key.setData(keyBytes.value());
      }
      assertEquals(index.equalityIndex.containsID(null, key, entryID),
        assertEquals(titleIndex.equalityIndex.containsID(null, key, entryID),
          ConditionResult.TRUE);
      }
      addKeys = new HashSet<ASN1OctetString>();
      substringIndexer = new SubstringIndexer(index.getAttributeType(),
                   index.getConfiguration().getSubstringLength());
      equalityIndexer = new EqualityIndexer(nameIndex.getAttributeType());
      equalityIndexer.indexEntry(null, entry, addKeys);
      key = new DatabaseEntry();
      for (ASN1OctetString keyBytes : addKeys) {
        key.setData(keyBytes.value());
        assertEquals(nameIndex.equalityIndex.containsID(null, key, entryID),
          ConditionResult.TRUE);
      }
      addKeys = new HashSet<ASN1OctetString>();
      substringIndexer = new SubstringIndexer(titleIndex.getAttributeType(),
                   titleIndex.getConfiguration().getSubstringLength());
      substringIndexer.indexEntry(null, entry, addKeys);
      key = new DatabaseEntry();
      for (ASN1OctetString keyBytes : addKeys) {
        key.setData(keyBytes.value());
      }
      assertEquals(index.substringIndex.containsID(null, key, entryID),
        assertEquals(titleIndex.substringIndex.containsID(null, key, entryID),
          ConditionResult.TRUE);
    }
      addKeys = new HashSet<ASN1OctetString>();
      substringIndexer = new SubstringIndexer(nameIndex.getAttributeType(),
                   nameIndex.getConfiguration().getSubstringLength());
      substringIndexer.indexEntry(null, entry, addKeys);
      key = new DatabaseEntry();
      for (ASN1OctetString keyBytes : addKeys) {
        key.setData(keyBytes.value());
        assertEquals(nameIndex.substringIndex.containsID(null, key, entryID),
          ConditionResult.TRUE);
      }
    }
    finally
    {
      ec.sharedLock.unlock();
opends/tests/unit-tests-testng/src/server/org/opends/server/types/EntrySchemaCheckingTestCase.java
@@ -1430,5 +1430,35 @@
    invalidReason = new MessageBuilder();
    failOnlyForStrictEvaluation(e);
  }
  /**
   * Tests schema checking for an entry that includes an attribute not
   * defined in any objectClasses but the subtypes of the attribute are.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testInvalidSuperiorAttribute()
         throws Exception
  {
    // The LDIF reader won't let us do this directly, so we have to hack around
    // it.
    Entry e = TestCaseUtils.makeEntry(
         "dn: uid=test.user,o=test",
         "objectClass: top",
         "objectClass: person",
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "objectClass: account",
         "uid: test.user",
         "givenName: Test",
         "sn: User",
         "cn: Test User");
    e.addAttribute(new Attribute("name", "foo"),
                   new LinkedList<AttributeValue>());
    assertFalse(e.conformsToSchema(null, false, true, true, new MessageBuilder()));
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java
@@ -343,6 +343,7 @@
    assertTrue(e.hasAttribute(ocType));
    assertTrue(e.hasAttribute(cnType));
    assertTrue(e.hasAttribute(nameType));
    assertFalse(e.hasAttribute(nameType, false));
    assertFalse(e.hasAttribute(uidType));
    assertTrue(e.hasAttribute(mnType));
@@ -350,6 +351,7 @@
    assertTrue(e.hasAttribute(ocType, options));
    assertTrue(e.hasAttribute(cnType, options));
    assertTrue(e.hasAttribute(nameType, options));
    assertFalse(e.hasAttribute(nameType, false, options));
    assertFalse(e.hasAttribute(uidType, options));
    assertTrue(e.hasAttribute(mnType, options));
@@ -357,6 +359,7 @@
    assertTrue(e.hasAttribute(ocType, options));
    assertTrue(e.hasAttribute(cnType, options));
    assertTrue(e.hasAttribute(nameType, options));
    assertFalse(e.hasAttribute(nameType, false, options));
    assertFalse(e.hasAttribute(uidType, options));
    assertTrue(e.hasAttribute(mnType, options));
@@ -364,6 +367,7 @@
    assertFalse(e.hasAttribute(ocType, options));
    assertTrue(e.hasAttribute(cnType, options));
    assertTrue(e.hasAttribute(nameType, options));
    assertFalse(e.hasAttribute(nameType, false, options));
    assertFalse(e.hasAttribute(uidType, options));
    assertFalse(e.hasAttribute(mnType, options));
@@ -371,6 +375,7 @@
    assertFalse(e.hasAttribute(ocType, options));
    assertFalse(e.hasAttribute(cnType, options));
    assertFalse(e.hasAttribute(nameType, options));
    assertFalse(e.hasAttribute(nameType, false, options));
    assertFalse(e.hasAttribute(uidType, options));
    assertFalse(e.hasAttribute(mnType, options));
@@ -379,6 +384,7 @@
    assertFalse(e.hasAttribute(ocType, options));
    assertFalse(e.hasAttribute(cnType, options));
    assertFalse(e.hasAttribute(nameType, options));
    assertFalse(e.hasAttribute(nameType, false, options));
    assertFalse(e.hasAttribute(uidType, options));
    assertFalse(e.hasAttribute(mnType, options));
  }
@@ -526,6 +532,9 @@
    assertNotNull(attrs);
    assertEquals(attrs.size(), 6);
    attrs = e.getAttribute(nameType, false);
    assertNull(attrs);
    attrs = e.getAttribute(uidType);
    assertNull(attrs);
@@ -563,6 +572,9 @@
    assertNotNull(attrs);
    assertEquals(attrs.size(), 6);
    attrs = e.getAttribute(nameType, false, options);
    assertNull(attrs);
    attrs = e.getAttribute(uidType, options);
    assertNull(attrs);
@@ -600,6 +612,9 @@
    assertNotNull(attrs);
    assertEquals(attrs.size(), 6);
    attrs = e.getAttribute(nameType, false, options);
    assertNull(attrs);
    attrs = e.getAttribute(uidType, options);
    assertNull(attrs);
@@ -636,6 +651,9 @@
    assertNotNull(attrs);
    assertEquals(attrs.size(), 3);
    attrs = e.getAttribute(nameType, false, options);
    assertNull(attrs);
    attrs = e.getAttribute(uidType, options);
    assertNull(attrs);