fixed SqmParameter type inference from an SqmPath during translation to SQL AST

This commit is contained in:
Steve Ebersole 2019-09-27 12:09:43 -05:00
parent b9f4562680
commit e572202cd1
20 changed files with 220 additions and 273 deletions

View File

@ -8,7 +8,7 @@
apply plugin: 'base'
ext {
ormVersion = new HibernateVersion( '6.0.0-SNAPSHOT', project )
ormVersion = new HibernateVersion( '6.0.0.BENCHMARK', project )
baselineJavaVersion = '1.8'
jpaVersion = new JpaVersion('2.2')
}

View File

@ -110,12 +110,15 @@ DOUBLE_PIPE : '||';
QUESTION_MARK : '?';
ARROW : '->';
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Keywords
ABS : [aA] [bB] [sS];
AS : [aA] [sS];
ALL : [aA] [lL] [lL];
AND : [aA] [nN] [dD];
ANY : [aA] [nN] [yY];
AS : [aA] [sS];
ASC : [aA] [sS] [cC];
AVG : [aA] [vV] [gG];
BY : [bB] [yY];
@ -129,11 +132,11 @@ COALESCE : [cC] [oO] [aA] [lL] [eE] [sS] [cC] [eE];
COLLATE : [cC] [oO] [lL] [lL] [aA] [tT] [eE];
CONCAT : [cC] [oO] [nN] [cC] [aA] [tT];
COUNT : [cC] [oO] [uU] [nN] [tT];
CROSS : [cC] [rR] [oO] [sS] [sS];
CURRENT_DATE : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [dD] [aA] [tT] [eE];
CURRENT_INSTANT : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [iI] [nN] [sS] [tT] [aA] [nN] [tT];
CURRENT_TIME : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [tT] [iI] [mM] [eE];
CURRENT_TIMESTAMP : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [tT] [iI] [mM] [eE] [sS] [tT] [aA] [mM] [pP];
CROSS : [cC] [rR] [oO] [sS] [sS];
DAY : [dD] [aA] [yY];
DELETE : [dD] [eE] [lL] [eE] [tT] [eE];
DESC : [dD] [eE] [sS] [cC];
@ -170,8 +173,8 @@ LEADING : [lL] [eE] [aA] [dD] [iI] [nN] [gG];
LEAST : [lL] [eE] [aA] [sS] [tT];
LEFT : [lL] [eE] [fF] [tT];
LENGTH : [lL] [eE] [nN] [gG] [tT] [hH];
LIMIT : [lL] [iI] [mM] [iI] [tT];
LIKE : [lL] [iI] [kK] [eE];
LIMIT : [lL] [iI] [mM] [iI] [tT];
LIST : [lL] [iI] [sS] [tT];
LN : [lL] [nN];
LOCATE : [lL] [oO] [cC] [aA] [tT] [eE];
@ -230,12 +233,12 @@ WHERE : [wW] [hH] [eE] [rR] [eE];
WITH : [wW] [iI] [tT] [hH];
YEAR : [yY] [eE] [aA] [rR];
ACOS : [aA] [cC] [oO] [sH];
ASIN : [aA] [sS] [iI] [nN];
ATAN : [aA] [cC] [oO] [sH];
ATAN : [aA] [tT] [aA] [nN];
ATAN2 : [aA] [tT] [aA] [nN] '2';
ACOS : [aA] [tT] [aA] [nN];
SIN : [sS] [iI] [nN];
COS : [cC] [oO] [sH];
SIN : [sS] [iI] [nN];
TAN : [tT] [aA] [nN];
// case-insensitive true, false and null recognition (split vote :)

View File

@ -761,7 +761,13 @@ trigFunction
;
trigFunctionName
: SIN | COS | TAN | ASIN | ACOS | ATAN
: COS
| SIN
| TAN
| ACOS
| ASIN
| ATAN
| ATAN2
;
atan2Function
@ -845,30 +851,43 @@ positionFunctionStringArgument
identifier
: IDENTIFIER
| (ABS
| AS
| ALL
| AND
| ANY
| AS
| ASC
| ATAN2
| AVG
| BY
| BETWEEN
| BOTH
| CASE
| CAST
| CEILING
| CLASS
| COALESCE
| COLLATE
| CONCAT
| COUNT
| CROSS
| CURRENT_DATE
| CURRENT_INSTANT
| CURRENT_TIME
| CURRENT_TIMESTAMP
| DAY
| DELETE
| DESC
| DISTINCT
| ELEMENTS
| ELSE
| EMPTY
| END
| ENTRY
| ESCAPE
| EXISTS
| EXP
| EXTRACT
| FETCH
| FLOOR
| FROM
| FOR
@ -882,6 +901,8 @@ identifier
| INDEX
| INNER
| INSERT
| INTO
| IS
| JOIN
| KEY
| LEADING
@ -892,38 +913,53 @@ identifier
| LIMIT
| LIST
| LN
| LOCATE
| LOWER
| MAP
| MAX
| MAXELEMENT
| MAXINDEX
| MEMBER
| MICROSECOND
| MILLISECOND
| MIN
| MINELEMENT
| MININDEX
| MINUTE
| MEMBER
| MOD
| MONTH
| NEW
| NOT
| NULLIF
| OBJECT
| OF
| OFFSET
| ON
| OR
| ORDER
| OUTER
| POSITION
| QUARTER
| POWER
| QUARTER
| REPLACE
| ROUND
| RIGHT
| SELECT
| ROUND
| SECOND
| SELECT
| SET
| SIGN
| SIZE
| SQRT
| STR
| SUBSTRING
| SUM
| THEN
| TIMEZONE_HOUR
| TIMEZONE_MINUTE
| TRAILING
| TREAT
| TRIM
| TYPE
| UPDATE
| UPPER
| VALUE

View File

@ -61,4 +61,11 @@ public class BasicSqmPathSource<J>
public Class<J> getJavaType() {
return getExpressableJavaTypeDescriptor().getJavaType();
}
@Override
public String toString() {
return "BasicSqmPathSource(" +
getPathName() + " : " + getJavaType().getSimpleName() +
")";
}
}

View File

@ -43,7 +43,6 @@ import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.metamodel.internal.JpaStaticMetaModelPopulationSetting;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.MappingModelExpressable;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
@ -59,8 +58,6 @@ import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.spi.PersisterFactory;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.PathException;
import org.hibernate.query.sqm.SqmExpressable;
import org.hibernate.type.BasicType;
import org.hibernate.type.Type;

View File

@ -6,6 +6,8 @@
*/
package org.hibernate.query.hql.internal;
import java.util.Arrays;
import org.jboss.logging.Logger;
import org.antlr.v4.runtime.CharStreams;

View File

@ -192,6 +192,8 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
executionContext.getDomainParameterBindingContext().getQueryParameterBindings(),
domainParameterXref,
jdbcParamsXref,
// todo (6.0) : ugh. this one is important
null,
session
);

View File

@ -15,6 +15,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.function.Consumer;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.Bindable;
import org.hibernate.metamodel.mapping.JdbcMapping;
@ -25,13 +26,13 @@ import org.hibernate.query.spi.QueryParameterBinding;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.spi.QueryParameterImplementor;
import org.hibernate.query.sqm.spi.JdbcParameterBySqmParameterAccess;
import org.hibernate.query.sqm.sql.SqlAstCreationState;
import org.hibernate.query.sqm.tree.SqmDmlStatement;
import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcParameter;
import org.hibernate.sql.exec.spi.JdbcParameterBinding;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
@ -107,50 +108,52 @@ public class SqmUtil {
return result;
}
public static JdbcParameterBindings buildJdbcParameterBindings(
SqmStatement sqmStatement,
JdbcParameterBySqmParameterAccess sqmInterpretation,
ExecutionContext executionContext) {
final DomainParameterXref domainParameterXref = DomainParameterXref.from( sqmStatement );
final Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamsXref =
generateJdbcParamsXref( domainParameterXref, sqmInterpretation );
return createJdbcParameterBindings(
executionContext.getDomainParameterBindingContext().getQueryParameterBindings(),
domainParameterXref,
jdbcParamsXref,
executionContext.getSession()
);
}
public static JdbcParameterBindings buildJdbcParameterBindings(
SqmStatement sqmStatement,
Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamsXref,
ExecutionContext executionContext) {
final DomainParameterXref domainParameterXref = DomainParameterXref.from( sqmStatement );
return createJdbcParameterBindings(
executionContext.getDomainParameterBindingContext().getQueryParameterBindings(),
domainParameterXref,
jdbcParamsXref,
executionContext.getSession()
);
}
// public static JdbcParameterBindings buildJdbcParameterBindings(
// SqmStatement sqmStatement,
// JdbcParameterBySqmParameterAccess sqmInterpretation,
// ExecutionContext executionContext) {
// final DomainParameterXref domainParameterXref = DomainParameterXref.from( sqmStatement );
// final Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamsXref =
// generateJdbcParamsXref( domainParameterXref, sqmInterpretation );
// return createJdbcParameterBindings(
// executionContext.getDomainParameterBindingContext().getQueryParameterBindings(),
// domainParameterXref,
// jdbcParamsXref,
// executionContext.getSession()
// );
// }
public static JdbcParameterBindings buildJdbcParameterBindings(
DomainParameterXref domainParameterXref,
Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamsXref,
ExecutionContext executionContext) {
return createJdbcParameterBindings(
executionContext.getDomainParameterBindingContext().getQueryParameterBindings(),
domainParameterXref,
jdbcParamsXref,
executionContext.getSession()
);
}
// public static JdbcParameterBindings buildJdbcParameterBindings(
// SqmStatement sqmStatement,
// Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamsXref,
// ExecutionContext executionContext) {
// final DomainParameterXref domainParameterXref = DomainParameterXref.from( sqmStatement );
// return createJdbcParameterBindings(
// executionContext.getDomainParameterBindingContext().getQueryParameterBindings(),
// domainParameterXref,
// jdbcParamsXref,
// executionContext.getSession()
// );
// }
// public static JdbcParameterBindings buildJdbcParameterBindings(
// DomainParameterXref domainParameterXref,
// Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamsXref,
// ExecutionContext executionContext) {
// return createJdbcParameterBindings(
// executionContext.getDomainParameterBindingContext().getQueryParameterBindings(),
// domainParameterXref,
// jdbcParamsXref,
// executionContext.getSession()
// );
// }
public static JdbcParameterBindings createJdbcParameterBindings(
QueryParameterBindings domainParamBindings,
DomainParameterXref domainParameterXref,
Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamXref,
SqlAstCreationState sqlAstCreationState,
SharedSessionContractImplementor session) {
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl();
@ -160,16 +163,18 @@ public class SqmUtil {
final List<SqmParameter> sqmParameters = entry.getValue();
final QueryParameterBinding<?> domainParamBinding = domainParamBindings.getBinding( queryParam );
final AllowableParameterType<?> parameterType = determineParameterType( domainParamBinding, queryParam, session );
final AllowableParameterType<?> parameterType = determineParameterType(
domainParamBinding,
queryParam,
session.getFactory()
);
final Map<SqmParameter, List<JdbcParameter>> jdbcParamMap = jdbcParamXref.get( queryParam );
for ( SqmParameter sqmParameter : sqmParameters ) {
final List<JdbcParameter> jdbcParams = jdbcParamMap.get( sqmParameter );
if ( ! domainParamBinding.isBound() ) {
final MappingModelExpressable mappingExpressable = session.getFactory()
.getDomainModel()
.resolveMappingExpressable( parameterType );
final MappingModelExpressable mappingExpressable = SqmMappingModelHelper.resolveMappingModelExpressable( sqmParameter, sqlAstCreationState );
mappingExpressable.visitJdbcTypes(
new Consumer<JdbcMapping>() {
int position = 0;
@ -264,10 +269,10 @@ public class SqmUtil {
);
}
private static AllowableParameterType determineParameterType(
public static AllowableParameterType determineParameterType(
QueryParameterBinding<?> binding,
QueryParameterImplementor<?> parameter,
SharedSessionContractImplementor session) {
SessionFactoryImplementor sessionFactory) {
if ( binding.getBindType() != null ) {
return binding.getBindType();
}
@ -276,7 +281,7 @@ public class SqmUtil {
return parameter.getHibernateType();
}
final TypeConfiguration typeConfiguration = session.getFactory().getTypeConfiguration();
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
// assume we have (or can create) a mapping for the parameter's Java type
return typeConfiguration.standardBasicTypeForJavaType( parameter.getParameterType() );

View File

@ -24,7 +24,10 @@ import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.MappingModelExpressable;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.model.domain.AllowableParameterType;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.internal.EmbeddedSqmPathSource;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.BinaryArithmeticOperator;
import org.hibernate.query.UnaryArithmeticOperator;
@ -740,35 +743,72 @@ public abstract class BaseSqmToSqlAstConverter
}
protected MappingModelExpressable<?> determineValueMapping(SqmExpression<?> sqmExpression) {
SqmExpressable<?> nodeType = sqmExpression.getNodeType();
if ( nodeType == null ) {
if ( sqmExpression instanceof SqmParameter ) {
final SqmParameter sqmParameter = (SqmParameter) sqmExpression;
final QueryParameterBinding<?> binding = domainParameterBindings.getBinding( domainParameterXref.getQueryParameter( sqmParameter ) );
assert binding != null;
nodeType = binding.getBindType();
}
if ( sqmExpression instanceof SqmParameter ) {
return determineValueMapping( (SqmParameter) sqmExpression );
}
MappingModelExpressable valueMapping = getCreationContext().getDomainModel().resolveMappingExpressable( nodeType );
if ( sqmExpression instanceof SqmPath ) {
log.debugf( "Determining mapping-model type for SqmPath : " + sqmExpression );
return SqmMappingModelHelper.resolveMappingModelExpressable( sqmExpression, this );
}
log.debugf( "Determining mapping-model type for generalized SqmExpression : " + sqmExpression );
final SqmExpressable<?> nodeType = sqmExpression.getNodeType();
final MappingModelExpressable valueMapping = getCreationContext().getDomainModel().resolveMappingExpressable( nodeType );
if ( valueMapping == null ) {
final Supplier<MappingModelExpressable> currentExpressableSupplier = inferableTypeAccessStack.getCurrent();
if ( currentExpressableSupplier != null ) {
valueMapping = currentExpressableSupplier.get();
return currentExpressableSupplier.get();
}
}
if ( valueMapping == null ) {
throw new ConversionException( "Could not determine ValueMapping for SqmParameter: " + sqmExpression );
throw new ConversionException( "Could not determine ValueMapping for SqmExpression: " + sqmExpression );
}
return valueMapping;
}
@SuppressWarnings("WeakerAccess")
protected MappingModelExpressable<?> determineValueMapping(SqmParameter<?> sqmParameter) {
log.debugf( "Determining mapping-model type for SqmParameter : " + sqmParameter );
final QueryParameterImplementor<?> queryParameter = domainParameterXref.getQueryParameter( sqmParameter );
final QueryParameterBinding<?> binding = domainParameterBindings.getBinding( queryParameter );
if ( sqmParameter.getAnticipatedType() == null ) {
// this should indicate the condition that the user query did not define an
// explicit type in regard to this parameter. Here we should prefer the
// inferable type and fallback to the binding type
final Supplier<MappingModelExpressable> currentExpressableSupplier = inferableTypeAccessStack.getCurrent();
if ( currentExpressableSupplier != null ) {
final MappingModelExpressable inferredMapping = currentExpressableSupplier.get();
if ( inferredMapping != null ) {
return inferredMapping;
}
}
}
AllowableParameterType<?> parameterSqmType = binding.getBindType();
if ( parameterSqmType == null ) {
parameterSqmType = queryParameter.getHibernateType();
if ( parameterSqmType == null ) {
parameterSqmType = sqmParameter.getAnticipatedType();
}
}
assert parameterSqmType != null;
if ( parameterSqmType instanceof BasicValuedMapping ) {
return (BasicValuedMapping) parameterSqmType;
}
throw new ConversionException( "Could not determine ValueMapping for SqmParameter: " + sqmParameter );
}
private final Stack<Supplier<MappingModelExpressable>> inferableTypeAccessStack = new StandardStack<>(
() -> null
);

View File

@ -33,16 +33,16 @@ public abstract class AbstractSqmExpression<T> extends AbstractJpaSelection<T> i
@Override
public final void applyInferableType(SqmExpressable<?> type) {
if ( type == null ) {
return;
}
final SqmExpressable<?> oldType = getNodeType();
final SqmExpressable<?> newType = highestPrecedenceType( oldType, type );
if ( newType != null && newType != oldType ) {
internalApplyInferableType( newType );
}
// if ( type == null ) {
// return;
// }
//
// final SqmExpressable<?> oldType = getNodeType();
//
// final SqmExpressable<?> newType = highestPrecedenceType( oldType, type );
// if ( newType != null && newType != oldType ) {
// internalApplyInferableType( newType );
// }
}
protected void internalApplyInferableType(SqmExpressable<?> newType) {

View File

@ -107,7 +107,7 @@ public class SqmEnumLiteral implements SqmExpression<Enum>, SqmExpressable<Enum>
@Override
public void applyInferableType(SqmExpressable<?> type) {
//noinspection unchecked
this.expressable = (SqmExpressable) type;
// this.expressable = (SqmExpressable) type;
}
@Override

View File

@ -12,6 +12,7 @@ import java.util.Collection;
import java.util.function.Consumer;
import javax.persistence.criteria.Expression;
import org.hibernate.annotations.Remove;
import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.query.criteria.JpaExpression;
@ -44,7 +45,11 @@ public interface SqmExpression<T> extends SqmSelectableNode<T>, JpaExpression<T>
* @apiNote The SqmExpressable type parameter is dropped here because
* the inference could technically cause a change in Java type (i.e.
* an implicit cast)
*
* @deprecated - type inference is now handled during the SQM -> SQL AST transformation
*/
@Remove
@Deprecated
void applyInferableType(SqmExpressable<?> type);
@Override

View File

@ -97,7 +97,7 @@ public class SqmFieldLiteral<T> implements SqmExpression<T>, SqmExpressable<T>,
@Override
public void applyInferableType(SqmExpressable<?> type) {
//noinspection unchecked
this.expressable = (SqmExpressable) type;
// this.expressable = (SqmExpressable) type;
}
@Override

View File

@ -41,6 +41,11 @@ public class SqmNamedParameter<T> extends AbstractSqmParameter<T> {
return ":" + getName();
}
@Override
public String toString() {
return "SqmNamedParameter(" + getName() + ")";
}
@Override
public String getName() {
return name;

View File

@ -49,6 +49,11 @@ public class SqmPositionalParameter<T> extends AbstractSqmParameter<T> {
return walker.visitPositionalParameterExpression( this );
}
@Override
public String toString() {
return "SqmPositionalParameter(" + getPosition() + ")";
}
@Override
public String asLoggableText() {
return "?" + getPosition();

View File

@ -232,8 +232,8 @@ public class SqmSubQuery<T> extends AbstractSqmSelectQuery<T> implements SqmSele
@Override
public void applyInferableType(SqmExpressable<?> type) {
//noinspection unchecked
this.expressableType = (SqmExpressable) type;
setResultType( type == null ? null : type.getExpressableJavaTypeDescriptor().getJavaType() );
// this.expressableType = (SqmExpressable) type;
// setResultType( type == null ? null : type.getExpressableJavaTypeDescriptor().getJavaType() );
}
@Override

View File

@ -1,37 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.test.query.hql;
import org.hibernate.boot.MetadataSources;
import org.hibernate.jpa.spi.JpaCompliance;
import org.hibernate.testing.junit5.SessionFactoryBasedFunctionalTest;
import org.hibernate.testing.orm.domain.helpdesk.HelpDeskDomainModel;
import org.hibernate.testing.orm.domain.retail.RetailDomainModel;
/**
* Tests for type inference outside of what JPA says should be supported.
*
* NOTE : Distinguishing this from {@link JpaStandardSqmInferenceTests} allows
* applying {@link JpaCompliance#isJpaQueryComplianceEnabled()} testing for just
* these extensions
*
* @see JpaStandardSqmInferenceTests
*
* @author Steve Ebersole
*/
public class ExtensionSqmInferenceTests extends SessionFactoryBasedFunctionalTest {
@Override
protected void applyMetadataSources(MetadataSources metadataSources) {
super.applyMetadataSources( metadataSources );
HelpDeskDomainModel.INSTANCE.applyDomainModel( metadataSources );
RetailDomainModel.INSTANCE.applyDomainModel( metadataSources );
}
// todo (6.0) : add the checks ;)
}

View File

@ -1,139 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.test.query.hql;
import javax.money.MonetaryAmount;
import org.hibernate.boot.MetadataSources;
import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.testing.junit5.SessionFactoryBasedFunctionalTest;
import org.hibernate.testing.orm.domain.helpdesk.HelpDeskDomainModel;
import org.hibernate.testing.orm.domain.helpdesk.Status;
import org.hibernate.testing.orm.domain.retail.RetailDomainModel;
import org.junit.jupiter.api.Test;
import static org.junit.Assert.assertEquals;
/**
* Tests for "type inference specifically limited to the cases JPA
* says should be supported.
*
* @see ExtensionSqmInferenceTests
*
* @author Steve Ebersole
*/
@SuppressWarnings("WeakerAccess")
public class JpaStandardSqmInferenceTests extends SessionFactoryBasedFunctionalTest {
protected void applyMetadataSources(MetadataSources metadataSources) {
super.applyMetadataSources( metadataSources );
HelpDeskDomainModel.INSTANCE.applyDomainModel( metadataSources );
RetailDomainModel.INSTANCE.applyDomainModel( metadataSources );
}
@Test
public void testEnumInference() {
checkParameters(
"from Account a where a.loginStatus = :status",
Status.class
);
checkParameters(
"from Account a where a.loginStatus <> :status",
Status.class
);
checkParameters(
"from Account a where a.loginStatus != :status",
Status.class
);
checkParameters(
"from Account a where a.loginStatus > :status",
Status.class
);
checkParameters(
"from Account a where a.loginStatus >= :status",
Status.class
);
checkParameters(
"from Account a where a.loginStatus < :status",
Status.class
);
checkParameters(
"from Account a where a.loginStatus <= :status",
Status.class
);
}
@Test
public void testConvertedInference() {
checkParameters(
"select l from LineItem l where l.subTotal = :limit",
MonetaryAmount.class
);
checkParameters(
"select l from LineItem l where l.subTotal <> :limit",
MonetaryAmount.class
);
checkParameters(
"select l from LineItem l where l.subTotal != :limit",
MonetaryAmount.class
);
checkParameters(
"select l from LineItem l where l.subTotal > :limit",
MonetaryAmount.class
);
checkParameters(
"select l from LineItem l where l.subTotal >= :limit",
MonetaryAmount.class
);
checkParameters(
"select l from LineItem l where l.subTotal < :limit",
MonetaryAmount.class
);
checkParameters(
"select l from LineItem l where l.subTotal <= :limit",
MonetaryAmount.class
);
}
private void checkParameters(String query, Class<?>... expecteds) {
final SqmStatement sqmStatement = sessionFactory().getQueryEngine().getHqlTranslator().interpret( query );
checkParameterTypes( sqmStatement, expecteds );
}
private void checkParameterTypes(SqmStatement<?> sqmStatement, Class<?>[] expectedParameterTypes) {
assertEquals( expectedParameterTypes.length, sqmStatement.getSqmParameters().size() );
int count = 0;
for ( SqmParameter<?> queryParameter : sqmStatement.getSqmParameters() ) {
assertEquals(
"Anticipated type for query parameter [" + queryParameter + "]",
expectedParameterTypes[count++],
queryParameter.getAnticipatedType().getExpressableJavaTypeDescriptor().getJavaType()
);
// assertThat(
// queryParameter.getAnticipatedType().getJavaType(),
// AssignableMatcher.assignableTo( expectedParameterTypes[count++] )
// );
}
}
}

View File

@ -74,13 +74,13 @@ public class ParameterTests extends BaseSqmUnitTest {
assertThat( sqm.getSqmParameters(), hasSize( 1 ) );
}
@Test
public void testAnticipatedTypeHandling() {
final SqmSelectStatement<?> sqm = interpretSelect( "select a.nickName from Person a where a.numberOfToes = ?1" );
final SqmParameter parameter = sqm.getSqmParameters().iterator().next();
assertThat( parameter.getAnticipatedType(), is( instanceOf( BasicSqmPathSource.class ) ) );
assertThat( parameter.allowMultiValuedBinding(), is( false ) );
}
// @Test
// public void testAnticipatedTypeHandling() {
// final SqmSelectStatement<?> sqm = interpretSelect( "select a.nickName from Person a where a.numberOfToes = ?1" );
// final SqmParameter parameter = sqm.getSqmParameters().iterator().next();
// assertThat( parameter.getAnticipatedType(), is( instanceOf( BasicSqmPathSource.class ) ) );
// assertThat( parameter.allowMultiValuedBinding(), is( false ) );
// }
@Test
public void testAllowMultiValuedBinding() {
@ -132,14 +132,14 @@ public class ParameterTests extends BaseSqmUnitTest {
final SqmSelectStatement<?> sqm = interpretSelect( "select p.id from Person p where p.name.first = :fname" );
assertThat( sqm.getSqmParameters().size(), equalTo( 1 ) );
final SqmParameter<?> parameter = sqm.getSqmParameters().iterator().next();
assertThat( parameter.getAnticipatedType(), instanceOf( BasicSqmPathSource.class ) );
// assertThat( parameter.getAnticipatedType(), instanceOf( BasicSqmPathSource.class ) );
}
{
final SqmSelectStatement<?> sqm = interpretSelect( "select p.id from Person p where p.name = :name" );
assertThat( sqm.getSqmParameters().size(), equalTo( 1 ) );
final SqmParameter<?> parameter = sqm.getSqmParameters().iterator().next();
assertThat( parameter.getAnticipatedType(), instanceOf( EmbeddedSqmPathSource.class ) );
// assertThat( parameter.getAnticipatedType(), instanceOf( EmbeddedSqmPathSource.class ) );
}
}

View File

@ -182,6 +182,22 @@ public class SmokeTests {
);
}
@Test
public void testHqlBasicParameterUsage(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final QueryImplementor<Component> query = session.createQuery(
"select e.component from SimpleEntity e where e.component.attribute1 = :param",
Component.class
);
final Component component = query.setParameter( "param", "a1" ).uniqueResult();
assertThat( component, notNullValue() );
assertThat( component.getAttribute1(), is( "a1" ) );
assertThat( component.getAttribute2(), is( "a2" ) );
}
);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Dynamic instantiations