diff --git a/opendj-server-legacy/src/main/java/org/opends/server/types/Entry.java b/opendj-server-legacy/src/main/java/org/opends/server/types/Entry.java index c8087de18f..edf19f7e4e 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/types/Entry.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/types/Entry.java @@ -13,7 +13,7 @@ * * Copyright 2006-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2016 ForgeRock AS. - * Portions Copyright 2023-2024 3A Systems, LLC. + * Portions Copyright 2023-2026 3A Systems, LLC. */ package org.opends.server.types; @@ -34,6 +34,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; @@ -663,9 +664,87 @@ public List getAllAttributes(AttributeType attributeType, return Collections.emptyList(); } + /** + * The set of attribute types having at least one subordinate type in a + * schema, cached per schema instance. Subtype-inclusive attribute lookups + * run for every entry read (compare assertions, search filters, password + * policy attributes, ...): when the requested type has no subordinates, + * walking every attribute of the entry degenerates to an exact map lookup, + * so cache which types actually need the walk. The schema and the set + * computed for it are held in one immutable holder swapped atomically, so + * a reader can never pair a set with the wrong schema. + */ + private static final class TypesWithSubordinates + { + private final Schema schema; + private final Set types; + + private TypesWithSubordinates(Schema schema, Set types) + { + this.schema = schema; + this.types = types; + } + } + + private static volatile TypesWithSubordinates typesWithSubordinates = + new TypesWithSubordinates(null, Collections. emptySet()); + + private static boolean mayHaveSubordinateTypes(AttributeType attrType) + { + if (attrType.isPlaceHolder()) + { + // isSuperTypeOf() matches place-holder types against schema-defined + // types by name, which an exact map lookup cannot honor: keep the walk. + return true; + } + Schema schema; + try + { + schema = DirectoryServer.getInstance().getServerContext().getSchema(); + } + catch (Exception e) + { + // No server schema available (offline tools, early startup): + // conservatively assume subtypes may exist. + return true; + } + if (schema == null) + { + return true; + } + TypesWithSubordinates cached = typesWithSubordinates; + if (cached.schema != schema) + { + Set withSubordinates = new HashSet<>(); + for (AttributeType type : schema.getAttributeTypes()) + { + for (AttributeType superior = type.getSuperiorType(); superior != null; + superior = superior.getSuperiorType()) + { + withSubordinates.add(superior); + } + } + cached = new TypesWithSubordinates(schema, withSubordinates); + typesWithSubordinates = cached; + } + return cached.types.contains(attrType); + } + private void addAttributeTypeOrSubTypeValue(Collection results, AttributeType attrType, Map> attrsMap) { + if (!mayHaveSubordinateTypes(attrType)) + { + // No type in the schema extends attrType: isSuperTypeOf() can only + // match the type itself, so use an exact lookup instead of walking + // every attribute of the entry. + List attributes = attrsMap.get(attrType); + if (attributes != null) + { + results.addAll(attributes); + } + return; + } for (Map.Entry> mapEntry : attrsMap.entrySet()) { if (attrType.isSuperTypeOf(mapEntry.getKey())) @@ -678,6 +757,21 @@ private void addAttributeTypeOrSubTypeValue(Collection results, Attri private void addAttributeTypeOrSubTypeValue(Collection results, AttributeDescription attrDesc, Map> attrsMap) { + if (!mayHaveSubordinateTypes(attrDesc.getAttributeType())) + { + List attributes = attrsMap.get(attrDesc.getAttributeType()); + if (attributes != null) + { + for (Attribute attribute : attributes) + { + if (attrDesc.isSuperTypeOf(attribute.getAttributeDescription())) + { + results.add(attribute); + } + } + } + return; + } for (Map.Entry> mapEntry : attrsMap.entrySet()) { if (!attrDesc.getAttributeType().isSuperTypeOf(mapEntry.getKey())) @@ -697,6 +791,23 @@ private void addAttributeTypeOrSubTypeValue(Collection results, Attri private boolean hasAttributeOrSubType(AttributeDescription attrDesc, Map> attrsMap) { + if (!mayHaveSubordinateTypes(attrDesc.getAttributeType())) + { + List attributes = attrsMap.get(attrDesc.getAttributeType()); + if (attributes != null) + { + for (Attribute attribute : attributes) + { + // It's possible that there could be an attribute without any values, + // which we should treat as not having the requested attribute. + if (!attribute.isEmpty() && attrDesc.isSuperTypeOf(attribute.getAttributeDescription())) + { + return true; + } + } + } + return false; + } for (Map.Entry> mapEntry : attrsMap.entrySet()) { if (!attrDesc.getAttributeType().isSuperTypeOf(mapEntry.getKey()))