Implement member of predicate for collection element

This commit is contained in:
Andrea Boriero 2020-07-29 15:31:16 +01:00
parent 59415fc190
commit 5ac6ab2751
11 changed files with 271 additions and 19 deletions

View File

@ -373,7 +373,7 @@ predicate
| expression (NOT)? LIKE expression (likeEscape)? # LikePredicate
| expression comparisonOperator expression # ComparisonPredicate
| EXISTS expression # ExistsPredicate
| MEMBER OF path # MemberOfPredicate
| expression MEMBER OF path # MemberOfPredicate
| NOT predicate # NegatedPredicate
| predicate AND predicate # AndPredicate
| predicate OR predicate # OrPredicate

View File

@ -15,6 +15,7 @@ import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.ColumnConsumer;
import org.hibernate.metamodel.mapping.ConvertibleModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
@ -238,9 +239,8 @@ public class BasicValuedCollectionPart
action.accept( getJdbcMapping() );
}
//
// @Override
// public BasicType getBasicType() {
// return mapper;
// }
@Override
public void visitColumns(ColumnConsumer consumer) {
consumer.accept( tableExpression, columnExpression, false, getJdbcMapping() );
}
}

View File

@ -513,7 +513,7 @@ public class QuerySplitter {
@Override
public SqmMemberOfPredicate visitMemberOfPredicate(SqmMemberOfPredicate predicate) {
final SqmPath pathCopy = sqmPathCopyMap.get( predicate.getPluralPath().getNavigablePath() );
return new SqmMemberOfPredicate( pathCopy, predicate.nodeBuilder() );
return new SqmMemberOfPredicate( predicate.getLeftHandExpression(), pathCopy, predicate.nodeBuilder() );
}
@Override

View File

@ -1282,7 +1282,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
final SqmPath sqmPluralPath = consumeDomainPath( ctx.path() );
if ( sqmPluralPath.getReferencedPathSource() instanceof PluralPersistentAttribute ) {
return new SqmMemberOfPredicate( sqmPluralPath, creationContext.getNodeBuilder() );
return new SqmMemberOfPredicate( (SqmExpression) ctx.expression().accept( this ), sqmPluralPath, creationContext.getNodeBuilder() );
}
else {
throw new SemanticException( "Path argument to MEMBER OF must be a plural attribute" );

View File

@ -16,10 +16,12 @@ import java.util.function.Supplier;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.dialect.function.TimestampaddFunction;
import org.hibernate.dialect.function.TimestampdiffFunction;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.internal.util.MutableInteger;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.internal.util.collections.StandardStack;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
@ -55,6 +57,7 @@ import org.hibernate.query.sqm.spi.JdbcParameterBySqmParameterAccess;
import org.hibernate.query.sqm.sql.internal.BasicValuedPathInterpretation;
import org.hibernate.query.sqm.sql.internal.EmbeddableValuedPathInterpretation;
import org.hibernate.query.sqm.sql.internal.EntityValuedPathInterpretation;
import org.hibernate.query.sqm.sql.internal.PluralValuedSimplePathInterpretation;
import org.hibernate.query.sqm.sql.internal.SqlAstQuerySpecProcessingStateImpl;
import org.hibernate.query.sqm.sql.internal.SqmParameterInterpretation;
import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation;
@ -113,6 +116,7 @@ import org.hibernate.query.sqm.tree.predicate.SqmGroupedPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmInListPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmInSubQueryPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmLikePredicate;
import org.hibernate.query.sqm.tree.predicate.SqmMemberOfPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmNegatedPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmNullnessPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmOrPredicate;
@ -143,6 +147,7 @@ import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
import org.hibernate.sql.ast.tree.expression.CastTarget;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Distinct;
import org.hibernate.sql.ast.tree.expression.Duration;
import org.hibernate.sql.ast.tree.expression.DurationUnit;
@ -166,6 +171,7 @@ import org.hibernate.sql.ast.tree.predicate.InListPredicate;
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
import org.hibernate.sql.ast.tree.predicate.Junction;
import org.hibernate.sql.ast.tree.predicate.LikePredicate;
import org.hibernate.sql.ast.tree.predicate.MemberOfPredicate;
import org.hibernate.sql.ast.tree.predicate.NegatedPredicate;
import org.hibernate.sql.ast.tree.predicate.NullnessPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate;
@ -177,6 +183,7 @@ import org.hibernate.sql.exec.internal.JdbcParameterImpl;
import org.hibernate.sql.exec.internal.JdbcParametersImpl;
import org.hibernate.sql.exec.spi.JdbcParameters;
import org.hibernate.sql.results.internal.SqlSelectionImpl;
import org.hibernate.type.spi.TypeConfiguration;
import org.jboss.logging.Logger;
@ -965,7 +972,7 @@ public abstract class BaseSqmToSqlAstConverter
@Override
public SqmPathInterpretation<?> visitPluralValuedPath(SqmPluralValuedSimplePath sqmPath) {
return (SqmPathInterpretation) sqmPath;
return PluralValuedSimplePathInterpretation.from( sqmPath, this, this );
}
@ -2161,6 +2168,70 @@ public abstract class BaseSqmToSqlAstConverter
return disjunction;
}
@Override
public MemberOfPredicate visitMemberOfPredicate(SqmMemberOfPredicate predicate) {
inferableTypeAccessStack.push( () -> determineValueMapping( predicate.getLeftHandExpression() ) );
final Expression lhs;
try {
lhs = (Expression) predicate.getLeftHandExpression().accept( this );
}
finally {
inferableTypeAccessStack.pop();
}
final SqmPath<?> pluralPath = predicate.getPluralPath();
final PluralAttributeMapping mappingModelExpressable = (PluralAttributeMapping) SqmMappingModelHelper.resolveMappingModelExpressable(
pluralPath,
getCreationContext().getDomainModel(),
getFromClauseAccess()::findTableGroup
);
return new MemberOfPredicate(
lhs,
predicate.isNegated(),
createSubQuery( pluralPath, mappingModelExpressable )
);
}
private QuerySpec createSubQuery(SqmPath<?> pluralPath, PluralAttributeMapping mappingModelExpressable) {
QuerySpec querySpec = new QuerySpec( true );
final TableGroup rootTableGroup = mappingModelExpressable.createRootTableGroup(
pluralPath.getNavigablePath(),
null,
true,
LockOptions.NONE.getLockMode(),
sqlAliasBaseManager,
getSqlExpressionResolver(),
() -> querySpec::applyPredicate,
creationContext
);
final MutableInteger count = new MutableInteger();
mappingModelExpressable.getElementDescriptor().visitColumns(
( (containingTableExpression, columnExpression, isColumnExpressionFormula, jdbcMapping) -> {
ColumnReference columnReference = new ColumnReference(
rootTableGroup.getPrimaryTableReference(),
columnExpression,
isColumnExpressionFormula,
jdbcMapping,
creationContext.getSessionFactory()
);
final int valuesPosition = count.getAndIncrement();
querySpec.getSelectClause().addSqlSelection(
new SqlSelectionImpl(
valuesPosition + 1,
valuesPosition,
columnReference
)
);
} )
);
querySpec.getFromClause().addRoot( rootTableGroup );
return querySpec;
}
@Override
public NegatedPredicate visitNegatedPredicate(SqmNegatedPredicate predicate) {
return new NegatedPredicate(

View File

@ -0,0 +1,111 @@
package org.hibernate.query.sqm.sql.internal;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.update.Assignment;
/**
* @author Andrea Boriero
*/
public class PluralValuedSimplePathInterpretation<T>
implements AssignableSqmPathInterpretation<T>, DomainResultProducer<T> {
public static SqmPathInterpretation<?> from(
SqmPluralValuedSimplePath sqmPath,
SqmToSqlAstConverter converter,
SemanticQueryWalker sqmWalker) {
final TableGroup tableGroup = converter.getFromClauseAccess().findTableGroup( sqmPath.getLhs()
.getNavigablePath() );
final PluralAttributeMapping mapping = (PluralAttributeMapping) tableGroup.getModelPart().findSubPart(
sqmPath.getReferencedPathSource().getPathName(),
null
);
return new PluralValuedSimplePathInterpretation<>(
null,
sqmPath,
mapping,
tableGroup
);
}
private final Expression sqlExpression;
private final SqmPluralValuedSimplePath<T> sqmPath;
private final PluralAttributeMapping mapping;
private final TableGroup tableGroup;
private PluralValuedSimplePathInterpretation(
Expression sqlExpression,
SqmPluralValuedSimplePath sqmPath,
PluralAttributeMapping mapping,
TableGroup tableGroup) {
this.sqlExpression = sqlExpression;
assert sqmPath != null;
this.sqmPath = sqmPath;
assert mapping != null;
this.mapping = mapping;
assert tableGroup != null;
this.tableGroup = tableGroup;
}
@Override
public void applySqlAssignments(
Expression newValueExpression,
AssignmentContext assignmentProcessingState,
Consumer<Assignment> assignmentConsumer,
SqlAstCreationContext creationContext) {
throw new NotYetImplementedFor6Exception( getClass() );
}
@Override
public NavigablePath getNavigablePath() {
return sqmPath.getNavigablePath();
}
@Override
public ModelPart getExpressionType() {
return mapping;
}
@Override
public void accept(SqlAstWalker sqlTreeWalker) {
sqlExpression.accept( sqlTreeWalker );
}
@Override
public List<ColumnReference> getColumnReferences() {
final List<ColumnReference> results = new ArrayList<>();
visitColumnReferences( results::add );
return results;
}
@Override
public void visitColumnReferences(Consumer<ColumnReference> columnReferenceConsumer) {
if ( sqlExpression instanceof ColumnReference ) {
columnReferenceConsumer.accept( (ColumnReference) sqlExpression );
}
else {
throw new NotYetImplementedFor6Exception( getClass() );
}
}
}

View File

@ -16,7 +16,7 @@ import org.hibernate.query.sqm.SemanticQueryWalker;
/**
* @author Steve Ebersole
*/
public class SqmGroupedPredicate extends AbstractSqmPredicate implements SqmPredicate {
public class SqmGroupedPredicate extends AbstractSqmPredicate {
private final SqmPredicate subPredicate;
public SqmGroupedPredicate(SqmPredicate subPredicate, NodeBuilder nodeBuilder) {

View File

@ -9,22 +9,33 @@ package org.hibernate.query.sqm.tree.predicate;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
/**
* @author Steve Ebersole
*/
public class SqmMemberOfPredicate extends AbstractNegatableSqmPredicate {
private final SqmExpression leftHandExpression;
private final SqmPath<?> pluralPath;
public SqmMemberOfPredicate(SqmPath<?> pluralPath, NodeBuilder nodeBuilder) {
this( pluralPath, false, nodeBuilder );
public SqmMemberOfPredicate(SqmExpression leftHandExpression, SqmPath<?> pluralPath, NodeBuilder nodeBuilder) {
this( leftHandExpression, pluralPath, false, nodeBuilder );
}
@SuppressWarnings("WeakerAccess")
public SqmMemberOfPredicate(SqmPath pluralPath, boolean negated, NodeBuilder nodeBuilder) {
public SqmMemberOfPredicate(
SqmExpression leftHandExpression,
SqmPath pluralPath,
boolean negated,
NodeBuilder nodeBuilder) {
super( negated, nodeBuilder );
this.pluralPath = pluralPath;
this.leftHandExpression = leftHandExpression;
}
public SqmExpression getLeftHandExpression() {
return leftHandExpression;
}
public SqmPath<?> getPluralPath() {

View File

@ -45,6 +45,7 @@ import org.hibernate.sql.ast.tree.predicate.InListPredicate;
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
import org.hibernate.sql.ast.tree.predicate.Junction;
import org.hibernate.sql.ast.tree.predicate.LikePredicate;
import org.hibernate.sql.ast.tree.predicate.MemberOfPredicate;
import org.hibernate.sql.ast.tree.predicate.NegatedPredicate;
import org.hibernate.sql.ast.tree.predicate.NullnessPredicate;
import org.hibernate.sql.ast.tree.predicate.SelfRenderingPredicate;
@ -150,4 +151,6 @@ public interface SqlAstWalker {
void visitDuration(Duration duration);
void visitConversion(Conversion conversion);
void visitMemberOfPredicate(MemberOfPredicate memberOfPredicate);
}

View File

@ -22,9 +22,7 @@ import org.hibernate.internal.FilterJdbcParameter;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.internal.util.collections.StandardStack;
import org.hibernate.metamodel.mapping.Association;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
@ -76,6 +74,7 @@ import org.hibernate.sql.ast.tree.predicate.InListPredicate;
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
import org.hibernate.sql.ast.tree.predicate.Junction;
import org.hibernate.sql.ast.tree.predicate.LikePredicate;
import org.hibernate.sql.ast.tree.predicate.MemberOfPredicate;
import org.hibernate.sql.ast.tree.predicate.NegatedPredicate;
import org.hibernate.sql.ast.tree.predicate.NullnessPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate;
@ -195,7 +194,7 @@ public abstract class AbstractSqlAstWalker
if ( fromClause == null || fromClause.getRoots().isEmpty() ) {
String fromDual = getDialect().getFromDual();
if ( !fromDual.isEmpty() ) {
appendSql(" ");
appendSql( " " );
appendSql( fromDual );
}
}
@ -221,7 +220,7 @@ public abstract class AbstractSqlAstWalker
appendSql( " order by " );
String separator = NO_SEPARATOR;
for (SortSpecification sortSpecification : sortSpecifications ) {
for ( SortSpecification sortSpecification : sortSpecifications ) {
appendSql( separator );
visitSortSpecification( sortSpecification );
separator = COMA_SEPARATOR;
@ -243,7 +242,7 @@ public abstract class AbstractSqlAstWalker
public void visitSortSpecification(SortSpecification sortSpecification) {
NullPrecedence nullPrecedence = sortSpecification.getNullPrecedence();
final boolean hasNullPrecedence = nullPrecedence != null && nullPrecedence != NullPrecedence.NONE;
if ( hasNullPrecedence && ! dialect.supportsNullPrecedence() ) {
if ( hasNullPrecedence && !dialect.supportsNullPrecedence() ) {
appendSql( "case when (" );
sortSpecification.getSortExpression().accept( this );
appendSql( ") is null then " );
@ -440,7 +439,7 @@ public abstract class AbstractSqlAstWalker
final TableGroup joinedGroup = tableGroupJoin.getJoinedGroup();
if ( joinedGroup instanceof VirtualTableGroup ) {
processTableGroupJoins( tableGroupJoin.getJoinedGroup());
processTableGroupJoins( tableGroupJoin.getJoinedGroup() );
}
else {
appendSql( EMPTY_STRING );
@ -923,6 +922,18 @@ public abstract class AbstractSqlAstWalker
);
}
@Override
public void visitMemberOfPredicate(MemberOfPredicate memberOfPredicate) {
memberOfPredicate.getLeftHandExpression().accept( this );
if ( memberOfPredicate.isNegated() ) {
appendSql( " not " );
}
appendSql( " in (" );
visitQuerySpec( memberOfPredicate.getQuerySpec() );
appendSql( ")" );
}
@Override
public void visitCaseSearchedExpression(CaseSearchedExpression caseSearchedExpression) {
dialect.getCaseExpressionWalker().visitCaseSearchedExpression( caseSearchedExpression, sqlBuffer, this );

View File

@ -0,0 +1,45 @@
package org.hibernate.sql.ast.tree.predicate;
import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.select.QuerySpec;
/**
* @author Andrea Boriero
*/
public class MemberOfPredicate implements Predicate {
private final Expression leftHandExpression;
private final QuerySpec querySpec;
private final boolean isNegated;
public MemberOfPredicate(
Expression leftHandExpression,
boolean isNegated,
QuerySpec querySpec) {
this.leftHandExpression = leftHandExpression;
this.isNegated = isNegated;
this.querySpec = querySpec;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public void accept(SqlAstWalker sqlTreeWalker) {
sqlTreeWalker.visitMemberOfPredicate( this );
}
public boolean isNegated() {
return isNegated;
}
public Expression getLeftHandExpression() {
return leftHandExpression;
}
public QuerySpec getQuerySpec() {
return querySpec;
}
}