Skip to content

Commit

Permalink
Cleaned up support for Equal(x, y) a bit. Not really finished yet.
Browse files Browse the repository at this point in the history
  • Loading branch information
mederly committed Dec 1, 2015
1 parent 1c37b6e commit e55b277
Show file tree
Hide file tree
Showing 16 changed files with 301 additions and 276 deletions.
Expand Up @@ -2779,7 +2779,7 @@ public void test910PreferredLanguageEqualsCostCenter() throws Exception {
}
}

@Test
@Test(enabled = false)
public void test915OrganizationEqualsCostCenter() throws Exception {
Session session = open();

Expand Down Expand Up @@ -2875,10 +2875,6 @@ public void test920DecisionsNotAnswered() throws Exception {
// TODO negative tests - order by entity, reference, any, collection
// TODO implement checks for "order by" for non-singletons

// TODO search for "no decision" condition (V2)
// TODO sorting based on referenced entity names (V2)
// TODO for cases: sorting based on object name, target name, campaign name (!) (V2-3)

protected <T extends Containerable> String getInterpretedQuery2(Session session, Class<T> type, File file) throws
Exception {
return getInterpretedQuery2(session, type, file, false);
Expand Down
@@ -0,0 +1,117 @@
/*
* Copyright (c) 2010-2015 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.evolveum.midpoint.repo.sql.query2;

import com.evolveum.midpoint.repo.sql.query2.definition.JpaDataNodeDefinition;
import com.evolveum.midpoint.repo.sql.query2.definition.JpaEntityDefinition;
import com.evolveum.midpoint.util.DebugDumpable;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import org.apache.commons.lang.Validate;

/**
* Describes result of ItemPath resolution: HQL property path + current data node definition.
* Points to parent data item (i.e. the one that corresponds to parent ItemPath translation),
* just in case we would like to go back via ".." operator.
*
* This differs from JpaDefinitions in that it points to a specific HQL property used in the
* query being constructed.
*
* This object is unmodifiable.
*
* @author mederly
*/
public class HqlDataInstance<D extends JpaDataNodeDefinition> implements DebugDumpable {

private static final Trace LOGGER = TraceManager.getTrace(HqlDataInstance.class);

final protected String hqlPath;
final protected D jpaDefinition;
final protected HqlDataInstance parentDataItem; // optional

public HqlDataInstance(String hqlPath, D jpaDefinition,
HqlDataInstance parentDataItem) {
Validate.notNull(hqlPath, "hqlPath");
Validate.notNull(jpaDefinition, "jpaDefinition");
this.hqlPath = hqlPath;
this.jpaDefinition = jpaDefinition;
this.parentDataItem = parentDataItem;
}

public String getHqlPath() {
return hqlPath;
}

public D getJpaDefinition() {
return jpaDefinition;
}

public HqlDataInstance getParentItem() {
return parentDataItem;
}

@Override
public String debugDump() {
return debugDump(0);
}

public String debugDumpNoParent() {
return debugDump(0, false);
}

@Override
public String debugDump(int indent) {
return debugDump(indent, true);
}

public String debugDump(int indent, boolean showParent) {
StringBuilder sb = new StringBuilder();
DebugUtil.indentDebugDump(sb, indent);
sb.append("HqlDataInstance:\n");
DebugUtil.indentDebugDump(sb, indent + 1);
sb.append("HQL path: ").append(hqlPath).append("\n");
DebugUtil.indentDebugDump(sb, indent + 1);
sb.append("JPA definition: ").append(jpaDefinition).append("\n");
DebugUtil.indentDebugDump(sb, indent + 1);
sb.append("Previous result: ");
if (parentDataItem != null) {
if (showParent) {
sb.append("\n");
sb.append(parentDataItem.debugDump(indent + 2));
} else {
sb.append(parentDataItem).append("\n");
}
} else {
sb.append("(null)\n");
}
return sb.toString();
}

@Override
public String toString() {
return "HqlDataInstance{" +
"hqlPath='" + hqlPath + '\'' +
", jpaDefinition=" + jpaDefinition +
", parentDataItem=(" + (parentDataItem != null ? parentDataItem.hqlPath : "none") +
")}";
}

public HqlEntityInstance asHqlEntityInstance() {
return new HqlEntityInstance(hqlPath, (JpaEntityDefinition) jpaDefinition, parentDataItem);
}
}
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2010-2015 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.evolveum.midpoint.repo.sql.query2;

import com.evolveum.midpoint.repo.sql.query2.definition.JpaEntityDefinition;
import org.apache.commons.lang.Validate;

/**
* @author mederly
*/
public class HqlEntityInstance extends HqlDataInstance<JpaEntityDefinition> {

public HqlEntityInstance(String hqlPath, JpaEntityDefinition jpaDefinition, HqlDataInstance parentPropertyPath) {
super(hqlPath, jpaDefinition, parentPropertyPath);
}

public HqlEntityInstance narrowFor(JpaEntityDefinition overridingDefinition) {
Validate.notNull(overridingDefinition, "overridingDefinition");

if (overridingDefinition.isAssignableFrom(jpaDefinition)) {
// nothing to do here
return this;
} else if (jpaDefinition.isAssignableFrom(overridingDefinition)) {
return new HqlEntityInstance(hqlPath, overridingDefinition, parentDataItem);
} else {
throw new IllegalStateException("Illegal attempt to narrow entity definition: from " + jpaDefinition +
" to " + overridingDefinition + ". These two definitions are not compatible.");
}
}

}
Expand Up @@ -17,10 +17,10 @@
package com.evolveum.midpoint.repo.sql.query2;

import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.path.ParentPathSegment;
import com.evolveum.midpoint.repo.sql.query.QueryException;
import com.evolveum.midpoint.repo.sql.query2.definition.JpaAnyDefinition;
import com.evolveum.midpoint.repo.sql.query2.definition.JpaDataNodeDefinition;
import com.evolveum.midpoint.repo.sql.query2.definition.JpaEntityDefinition;
import com.evolveum.midpoint.repo.sql.query2.definition.JpaLinkDefinition;
import com.evolveum.midpoint.util.DebugDumpable;
import com.evolveum.midpoint.util.DebugUtil;
Expand All @@ -31,109 +31,98 @@
/**
* Describes current state in ItemPath resolution.
*
* We know what we've already resolved, and what remains to be done.
* We know which data element we're pointing to (entity, property, reference, any) and which JPA expression we can use to address it.
* We know what remains to be resolved.
* We know the HQL item we are pointing to.
* We know last transition - how we got here.
*
* We also remember previous resolution state, in order to be able to go back via ".." path segment.
* This object is unmodifiable.
*
* @author mederly
*/
public class ItemPathResolutionState implements DebugDumpable {

private static final Trace LOGGER = TraceManager.getTrace(ItemPathResolutionState.class);

private ItemPath remainingItemPath;
final private ItemPath remainingItemPath;
final private HqlDataInstance hqlDataInstance;
final private JpaLinkDefinition lastTransition; // how we got here (optional)

private String currentHqlPath;
private JpaDataNodeDefinition currentJpaNode; // where we are
private JpaLinkDefinition lastTransition; // how we got here (optional)
final private ItemPathResolver itemPathResolver; // provides auxiliary functionality

private ItemPathResolutionState previousState; // from which state we got here (optional)

private ItemPathResolver itemPathResolver; // provides auxiliary functionality

public ItemPathResolutionState(ItemPath pathToResolve, String startingHqlPath, JpaEntityDefinition baseEntityDefinition, ItemPathResolver itemPathResolver) {
public ItemPathResolutionState(ItemPath pathToResolve, HqlDataInstance hqlDataInstance, ItemPathResolver itemPathResolver) {
Validate.notNull(pathToResolve, "pathToResolve");
Validate.notNull(startingHqlPath, "startingHqlPath");
Validate.notNull(baseEntityDefinition, "baseEntityDefinition");
Validate.notNull(hqlDataInstance, "hqlDataInstance");
Validate.notNull(itemPathResolver, "itemPathResolver");
this.remainingItemPath = pathToResolve;
this.currentHqlPath = startingHqlPath;
this.currentJpaNode = baseEntityDefinition;
this.hqlDataInstance = hqlDataInstance;
this.lastTransition = null;
this.previousState = null;
this.itemPathResolver = itemPathResolver;
}

// no validation as this is private
private ItemPathResolutionState(ItemPath remainingItemPath, String currentHqlPath, JpaDataNodeDefinition currentJpaNode, JpaLinkDefinition lastTransition, ItemPathResolutionState previousState, ItemPathResolver itemPathResolver) {
this.remainingItemPath = remainingItemPath;
this.currentHqlPath = currentHqlPath;
this.currentJpaNode = currentJpaNode;
this.lastTransition = lastTransition;
this.previousState = previousState;
this.itemPathResolver = itemPathResolver;
}

public ItemPath getRemainingItemPath() {
return remainingItemPath;
}

public String getCurrentHqlPath() {
return currentHqlPath;
}

public JpaDataNodeDefinition getCurrentJpaNode() {
return currentJpaNode;
public HqlDataInstance getHqlDataInstance() {
return hqlDataInstance;
}

public JpaLinkDefinition getLastTransition() {
return lastTransition;
}

public ItemPathResolutionState getPreviousState() {
return previousState;
}

public boolean hasPreviousState() {
return previousState != null;
public ItemPathResolver getItemPathResolver() {
return itemPathResolver;
}

public boolean isFinal() {
return ItemPath.isNullOrEmpty(remainingItemPath) || currentJpaNode instanceof JpaAnyDefinition;
return ItemPath.isNullOrEmpty(remainingItemPath) || hqlDataInstance.getJpaDefinition() instanceof JpaAnyDefinition;
}

/**
* Executes transition to next state. Modifies query context by adding joins as necessary.
*
* Precondition: !isFinal()
* Postcondition: non-null state
* Precondition: adequate transition exists
*
* @param singletonOnly Collections are forbidden
* @return destination state - always not null
*/
public ItemPathResolutionState nextState(boolean singletonOnly) throws QueryException {
DataSearchResult<JpaDataNodeDefinition> result = currentJpaNode.nextLinkDefinition(remainingItemPath);

// special case - ".." when having previous state means returning to that state
// used e.g. for Exists (some-path, some-conditions AND Equals(../xxx, yyy))
//
// This is brutal hack, to be thought again.
if (remainingItemPath.startsWith(ParentPathSegment.class) && hqlDataInstance.getParentItem() != null) {
ItemPathResolutionState next = new ItemPathResolutionState(
remainingItemPath.tail(),
hqlDataInstance.getParentItem(),
itemPathResolver);
return next;

}
DataSearchResult<JpaDataNodeDefinition> result = hqlDataInstance.getJpaDefinition().nextLinkDefinition(remainingItemPath);
LOGGER.trace("nextLinkDefinition on '{}' returned '{}'", remainingItemPath, result != null ? result.getLinkDefinition() : "(null)");
if (result == null) { // sorry we failed (however, this should be caught before -> so IllegalStateException)
throw new IllegalStateException("Couldn't find " + remainingItemPath + " in " + currentJpaNode);
throw new IllegalStateException("Couldn't find " + remainingItemPath + " in " + hqlDataInstance.getJpaDefinition());
}
JpaLinkDefinition linkDefinition = result.getLinkDefinition();
String newHqlPath = currentHqlPath;
String newHqlPath = hqlDataInstance.getHqlPath();
if (linkDefinition.hasJpaRepresentation()) {
if (singletonOnly && linkDefinition.isMultivalued()) {
throw new QueryException("Collections are not allowable for right-side paths"); // TODO better message + context
}
if (!linkDefinition.isEmbedded() || linkDefinition.isMultivalued()) {
LOGGER.trace("Adding join for '{}' to context", linkDefinition);
newHqlPath = itemPathResolver.addJoin(linkDefinition, currentHqlPath);
newHqlPath = itemPathResolver.addJoin(linkDefinition, hqlDataInstance.getHqlPath());
} else {
newHqlPath += "." + linkDefinition.getJpaName();
}
}
ItemPathResolutionState next = new ItemPathResolutionState(
result.getRemainder(),
newHqlPath,
result.getTargetDefinition(),
result.getLinkDefinition(),
this,
new HqlDataInstance(newHqlPath, result.getTargetDefinition(), hqlDataInstance),
itemPathResolver);
return next;
}
Expand All @@ -157,32 +146,17 @@ public String debugDump(int indent, boolean showParent) {
DebugUtil.indentDebugDump(sb, indent);
sb.append("ItemPathResolutionState: Remaining path: ").append(remainingItemPath).append("\n");
DebugUtil.indentDebugDump(sb, indent + 1);
sb.append("Current HQL path: ").append(currentHqlPath).append("\n");
DebugUtil.indentDebugDump(sb, indent + 1);
sb.append("Current JPA data node: ").append(currentJpaNode).append("\n");
DebugUtil.indentDebugDump(sb, indent + 1);
sb.append("Last transition: ").append(lastTransition).append("\n");
DebugUtil.indentDebugDump(sb, indent + 1);
sb.append("Previous state: ");
if (previousState != null) {
if (showParent) {
sb.append("\n");
sb.append(previousState.debugDump(indent + 2));
} else {
sb.append(previousState).append("\n");
}
} else {
sb.append("(null)\n");
}
sb.append("HQL data item: ").append(hqlDataInstance.debugDump(indent + 2, showParent));
return sb.toString();
}

@Override
public String toString() {
return "ItemPathResolutionState{" +
"remainingItemPath=" + remainingItemPath +
", currentHqlPath='" + currentHqlPath + '\'' +
", currentJpaNode=" + currentJpaNode +
", hqlDataInstance='" + hqlDataInstance + '\'' +
'}';
}

Expand Down

0 comments on commit e55b277

Please sign in to comment.