diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/GetOperationOptions.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/GetOperationOptions.java index 6d2276744f0..0162645a9ad 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/GetOperationOptions.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/GetOperationOptions.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Objects; /** * @author semancik @@ -124,6 +125,18 @@ public class GetOperationOptions extends AbstractOptions implements Serializable */ private Long staleness; + /** + * Should the results be made distinct. + * Not all providers support this option. + * + * BEWARE: + * - may bring a potentially huge performance penalty + * - may interfere with paging (!) + * + * So please consider this option an EXPERIMENTAL, for now. + */ + private Boolean distinct; + public RetrieveOption getRetrieve() { return retrieve; } @@ -436,6 +449,30 @@ public static boolean isMaxStaleness(GetOperationOptions options) { return GetOperationOptions.getStaleness(options) == Long.MAX_VALUE; } + public Boolean getDistinct() { + return distinct; + } + + public void setDistinct(Boolean distinct) { + this.distinct = distinct; + } + + public static boolean isDistinct(GetOperationOptions options) { + if (options == null) { + return false; + } + if (options.distinct == null) { + return false; + } + return options.distinct; + } + + public static GetOperationOptions createDistinct() { + GetOperationOptions opts = new GetOperationOptions(); + opts.setDistinct(true); + return opts; + } + public RelationalValueSearchQuery getRelationalValueSearchQuery() { return relationalValueSearchQuery; @@ -446,110 +483,31 @@ public void setRelationalValueSearchQuery(RelationalValueSearchQuery relationalV } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((allowNotFound == null) ? 0 : allowNotFound.hashCode()); - result = prime * result + ((doNotDiscovery == null) ? 0 : doNotDiscovery.hashCode()); - result = prime * result + ((noFetch == null) ? 0 : noFetch.hashCode()); - result = prime * result + ((raw == null) ? 0 : raw.hashCode()); - result = prime * result + ((readOnly == null) ? 0 : readOnly.hashCode()); - result = prime * result - + ((relationalValueSearchQuery == null) ? 0 : relationalValueSearchQuery.hashCode()); - result = prime * result + ((resolve == null) ? 0 : resolve.hashCode()); - result = prime * result + ((resolveNames == null) ? 0 : resolveNames.hashCode()); - result = prime * result + ((retrieve == null) ? 0 : retrieve.hashCode()); - result = prime * result + ((staleness == null) ? 0 : staleness.hashCode()); - result = prime * result + ((tolerateRawData == null) ? 0 : tolerateRawData.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof GetOperationOptions)) + return false; + GetOperationOptions that = (GetOperationOptions) o; + return retrieve == that.retrieve && + Objects.equals(resolve, that.resolve) && + Objects.equals(resolveNames, that.resolveNames) && + Objects.equals(noFetch, that.noFetch) && + Objects.equals(raw, that.raw) && + Objects.equals(tolerateRawData, that.tolerateRawData) && + Objects.equals(doNotDiscovery, that.doNotDiscovery) && + Objects.equals(relationalValueSearchQuery, that.relationalValueSearchQuery) && + Objects.equals(allowNotFound, that.allowNotFound) && + Objects.equals(readOnly, that.readOnly) && + Objects.equals(staleness, that.staleness) && + Objects.equals(distinct, that.distinct); } @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - GetOperationOptions other = (GetOperationOptions) obj; - if (allowNotFound == null) { - if (other.allowNotFound != null) { - return false; - } - } else if (!allowNotFound.equals(other.allowNotFound)) { - return false; - } - if (doNotDiscovery == null) { - if (other.doNotDiscovery != null) { - return false; - } - } else if (!doNotDiscovery.equals(other.doNotDiscovery)) { - return false; - } - if (noFetch == null) { - if (other.noFetch != null) { - return false; - } - } else if (!noFetch.equals(other.noFetch)) { - return false; - } - if (raw == null) { - if (other.raw != null) { - return false; - } - } else if (!raw.equals(other.raw)) { - return false; - } - if (readOnly == null) { - if (other.readOnly != null) { - return false; - } - } else if (!readOnly.equals(other.readOnly)) { - return false; - } - if (relationalValueSearchQuery == null) { - if (other.relationalValueSearchQuery != null) { - return false; - } - } else if (!relationalValueSearchQuery.equals(other.relationalValueSearchQuery)) { - return false; - } - if (resolve == null) { - if (other.resolve != null) { - return false; - } - } else if (!resolve.equals(other.resolve)) { - return false; - } - if (resolveNames == null) { - if (other.resolveNames != null) { - return false; - } - } else if (!resolveNames.equals(other.resolveNames)) { - return false; - } - if (retrieve != other.retrieve) { - return false; - } - if (staleness == null) { - if (other.staleness != null) { - return false; - } - } else if (!staleness.equals(other.staleness)) { - return false; - } - if (tolerateRawData == null) { - if (other.tolerateRawData != null) { - return false; - } - } else if (!tolerateRawData.equals(other.tolerateRawData)) { - return false; - } - return true; + public int hashCode() { + return Objects + .hash(retrieve, resolve, resolveNames, noFetch, raw, tolerateRawData, doNotDiscovery, relationalValueSearchQuery, + allowNotFound, readOnly, staleness, distinct); } public GetOperationOptions clone() { @@ -563,6 +521,7 @@ public GetOperationOptions clone() { clone.allowNotFound = this.allowNotFound; clone.readOnly = this.readOnly; clone.staleness = this.staleness; + clone.distinct = this.distinct; if (this.relationalValueSearchQuery != null) { clone.relationalValueSearchQuery = this.relationalValueSearchQuery.clone(); } @@ -581,6 +540,7 @@ public String toString() { appendFlag(sb, "allowNotFound", allowNotFound); appendFlag(sb, "readOnly", readOnly); appendVal(sb, "staleness", staleness); + appendVal(sb, "distinct", distinct); appendVal(sb, "relationalValueSearchQuery", relationalValueSearchQuery); removeLastComma(sb); sb.append(")"); @@ -606,7 +566,7 @@ public static Collection> fromRestOptions(L return rv; } - public static GetOperationOptions fromRestOptions(List options){ + public static GetOperationOptions fromRestOptions(List options) { if (options == null || options.isEmpty()){ return null; } diff --git a/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/RepositoryService.java b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/RepositoryService.java index b44fde7199b..9b04f9f958c 100644 --- a/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/RepositoryService.java +++ b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/RepositoryService.java @@ -350,6 +350,10 @@ SearchResultMetadata searchObjectsIterative(Class type int countObjects(Class type, ObjectQuery query, OperationResult parentResult) throws SchemaException; + int countObjects(Class type, ObjectQuery query, + Collection> options, + OperationResult parentResult) throws SchemaException; + boolean isAnySubordinate(String upperOrgOid, Collection lowerObjectOids) throws SchemaException; boolean isDescendant(PrismObject object, String orgOid) throws SchemaException; diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/RepositoryCache.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/RepositoryCache.java index 5e6d78784e4..0449d1bd717 100644 --- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/RepositoryCache.java +++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/RepositoryCache.java @@ -227,6 +227,15 @@ public int countObjects(Class type, ObjectQuery query, return repository.countObjects(type, query, parentResult); } + @Override + public int countObjects(Class type, ObjectQuery query, + Collection> options, OperationResult parentResult) + throws SchemaException { + // TODO use cached query result if applicable + log("Cache: PASS countObjects ({})", type.getSimpleName()); + return repository.countObjects(type, query, options, parentResult); + } + public void modifyObject(Class type, String oid, Collection modifications, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { modifyObject(type, oid, modifications, null, parentResult); diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java index 300b528b937..26afe274eed 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java @@ -423,6 +423,11 @@ public void deleteObject(Class type, String oid, Opera @Override public int countObjects(Class type, ObjectQuery query, OperationResult result) { + return countObjects(type, query, null, result); + } + + public int countObjects(Class type, ObjectQuery query, + Collection> options, OperationResult result) { Validate.notNull(type, "Object type must not be null."); Validate.notNull(result, "Operation result must not be null."); @@ -451,7 +456,7 @@ public int countObjects(Class type, ObjectQuery query, while (true) { try { - return objectRetriever.countObjectsAttempt(type, query, subResult); + return objectRetriever.countObjectsAttempt(type, query, options, subResult); } catch (RuntimeException ex) { attempt = baseHelper.logOperationAttempt(null, operation, attempt, ex, subResult); } diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java index ab519b6b341..9efa50db631 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java @@ -317,8 +317,9 @@ public PrismObject listAccountShadowOwnerAttempt(String accountOid, Op return userType; } - public int countObjectsAttempt(Class type, ObjectQuery query, OperationResult result) { - LOGGER_PERFORMANCE.debug("> count objects {}", new Object[]{type.getSimpleName()}); + public int countObjectsAttempt(Class type, ObjectQuery query, + Collection> options, OperationResult result) { + LOGGER_PERFORMANCE.debug("> count objects {}", type.getSimpleName()); int count = 0; @@ -329,6 +330,9 @@ public int countObjectsAttempt(Class type, ObjectQuery session = baseHelper.beginReadOnlyTransaction(); Number longCount; if (query == null || query.getFilter() == null) { + if (GetOperationOptions.isDistinct(SelectorOptions.findRootOptions(options))) { + throw new UnsupportedOperationException("Distinct option is not supported here"); // TODO + } // this is 5x faster than count with 3 inner joins, it can probably improved also for queries which // filters uses only properties from concrete entities like RUser, RRole by improving interpreter [lazyman] SQLQuery sqlQuery = session.createSQLQuery("SELECT COUNT(*) FROM " + RUtil.getTableName(hqlType)); @@ -337,7 +341,7 @@ public int countObjectsAttempt(Class type, ObjectQuery RQuery rQuery; if (isUseNewQueryInterpreter(query)) { QueryEngine2 engine = new QueryEngine2(getConfiguration(), prismContext); - rQuery = engine.interpret(query, type, null, true, session); + rQuery = engine.interpret(query, type, options, true, session); } else { QueryEngine engine = new QueryEngine(getConfiguration(), prismContext); rQuery = engine.interpret(query, type, null, true, session); diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/query2/QueryInterpreter2.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/query2/QueryInterpreter2.java index 1a7f69f97bd..d06838d4d0a 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/query2/QueryInterpreter2.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/query2/QueryInterpreter2.java @@ -120,11 +120,19 @@ public RootHibernateQuery interpret(ObjectQuery query, Class parameter : parameters.entrySet()) { @@ -132,7 +133,11 @@ public void setResultTransformer(ResultTransformer resultTransformer) { this.resultTransformer = resultTransformer; } - public Condition createIsNull(String propertyPath) { + public void setDistinct(boolean distinct) { + this.distinct = distinct; + } + + public Condition createIsNull(String propertyPath) { return new IsNullCondition(this, propertyPath); }