Skip to content

Commit

Permalink
Added support for 'length' and 'substring' as predicates and selections.
Browse files Browse the repository at this point in the history
  • Loading branch information
Naros committed Dec 5, 2018
1 parent 6387a88 commit 65b4782
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 45 deletions.
Expand Up @@ -24,12 +24,8 @@
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.internal.util.collections.StandardStack;
import org.hibernate.metamodel.model.domain.BasicDomainType;
import org.hibernate.metamodel.model.domain.internal.SingularPersistentAttributeEmbedded;
import org.hibernate.metamodel.model.domain.spi.AllowableParameterType;
import org.hibernate.metamodel.model.domain.spi.BasicTypeDescriptor;
import org.hibernate.metamodel.model.domain.spi.BasicValueMapper;
import org.hibernate.metamodel.model.domain.spi.BasicValuedNavigable;
import org.hibernate.metamodel.model.domain.spi.EntityIdentifier;
import org.hibernate.metamodel.model.domain.spi.EntityIdentifierComposite;
import org.hibernate.metamodel.model.domain.spi.EntityIdentifierSimple;
Expand Down Expand Up @@ -92,6 +88,7 @@
import org.hibernate.query.sqm.tree.expression.function.SqmMinFunction;
import org.hibernate.query.sqm.tree.expression.function.SqmModFunction;
import org.hibernate.query.sqm.tree.expression.function.SqmNullifFunction;
import org.hibernate.query.sqm.tree.expression.function.SqmSubstringFunction;
import org.hibernate.query.sqm.tree.expression.function.SqmSumFunction;
import org.hibernate.query.sqm.tree.expression.function.SqmTrimFunction;
import org.hibernate.query.sqm.tree.expression.function.SqmUpperFunction;
Expand Down Expand Up @@ -167,6 +164,7 @@
import org.hibernate.sql.ast.tree.spi.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.spi.expression.SqlTuple;
import org.hibernate.sql.ast.tree.spi.expression.SubQuery;
import org.hibernate.sql.ast.tree.spi.expression.SubstrFunction;
import org.hibernate.sql.ast.tree.spi.expression.SumFunction;
import org.hibernate.sql.ast.tree.spi.expression.TrimFunction;
import org.hibernate.sql.ast.tree.spi.expression.UnaryOperation;
Expand Down Expand Up @@ -203,8 +201,6 @@
import org.hibernate.sql.exec.spi.JdbcParameters;
import org.hibernate.sql.results.spi.DomainResultProducer;
import org.hibernate.sql.results.spi.SqlSelection;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.StandardBasicTypes.StandardBasicType;
import org.hibernate.type.spi.StandardSpiBasicTypes;
import org.hibernate.type.spi.TypeConfiguration;

Expand Down Expand Up @@ -1400,7 +1396,7 @@ public LengthFunction visitLengthFunction(SqmLengthFunction function) {

try {
return new LengthFunction(
(Expression) function.getArgument().accept( this ),
toExpression( function.getArgument().accept( this ) ),
getSessionFactory().getTypeConfiguration()
.getBasicTypeRegistry()
.getBasicType( Long.class )
Expand Down Expand Up @@ -1496,6 +1492,27 @@ public Object visitModFunction(SqmModFunction function) {
}
}

@Override
public Object visitSubstringFunction(SqmSubstringFunction expression) {
shallownessStack.push( Shallowness.FUNCTION );

try {
List<Expression> expressionList = new ArrayList<>();
expressionList.add( toExpression( expression.getSource().accept( this ) ) );
expressionList.add( toExpression( expression.getStartPosition().accept( this ) ) );
expressionList.add( toExpression( expression.getLength().accept( this ) ) );

return new SubstrFunction(
expression.getFunctionName(),
expressionList,
( (BasicValuedExpressableType) expression.getExpressableType() ).getSqlExpressableType()
);
}
finally {
shallownessStack.pop();
}
}

@Override
public SumFunction visitSumFunction(SqmSumFunction expression) {
shallownessStack.push( Shallowness.FUNCTION );
Expand Down Expand Up @@ -1659,7 +1676,7 @@ public Object visitTrimFunction(SqmTrimFunction expression) {
@Override
public Object visitUpperFunction(SqmUpperFunction sqmFunction) {
return new UpperFunction(
(Expression) sqmFunction.getArgument().accept( this ),
toExpression( sqmFunction.getArgument().accept( this ) ),
( (BasicValuedExpressableType) sqmFunction.getExpressableType() ).getSqlExpressableType()
);

Expand Down
Expand Up @@ -134,6 +134,7 @@
import org.hibernate.query.sqm.tree.expression.function.SqmCountFunction;
import org.hibernate.query.sqm.tree.expression.function.SqmCountStarFunction;
import org.hibernate.query.sqm.tree.expression.function.SqmGenericFunction;
import org.hibernate.query.sqm.tree.expression.function.SqmLengthFunction;
import org.hibernate.query.sqm.tree.expression.function.SqmLowerFunction;
import org.hibernate.query.sqm.tree.expression.function.SqmMaxFunction;
import org.hibernate.query.sqm.tree.expression.function.SqmMinFunction;
Expand Down Expand Up @@ -1272,18 +1273,6 @@ public Object visitGreaterThanPredicate(HqlParser.GreaterThanPredicateContext ct
final SqmExpression lhs = (SqmExpression) ctx.expression().get( 0 ).accept( this );
final SqmExpression rhs = (SqmExpression) ctx.expression().get( 1 ).accept( this );

if ( lhs.getInferableType() != null ) {
if ( rhs instanceof InferableTypeSqmExpression ) {
( (InferableTypeSqmExpression) rhs ).impliedType( lhs.getInferableType() );
}
}

if ( rhs.getInferableType() != null ) {
if ( lhs instanceof InferableTypeSqmExpression ) {
( (InferableTypeSqmExpression) lhs ).impliedType( rhs.getInferableType() );
}
}

return new RelationalSqmPredicate( RelationalPredicateOperator.GREATER_THAN, lhs, rhs );
}

Expand All @@ -1292,18 +1281,6 @@ public Object visitGreaterThanOrEqualPredicate(HqlParser.GreaterThanOrEqualPredi
final SqmExpression lhs = (SqmExpression) ctx.expression().get( 0 ).accept( this );
final SqmExpression rhs = (SqmExpression) ctx.expression().get( 1 ).accept( this );

if ( lhs.getInferableType() != null ) {
if ( rhs instanceof InferableTypeSqmExpression ) {
( (InferableTypeSqmExpression) rhs ).impliedType( lhs.getInferableType() );
}
}

if ( rhs.getInferableType() != null ) {
if ( lhs instanceof InferableTypeSqmExpression ) {
( (InferableTypeSqmExpression) lhs ).impliedType( rhs.getInferableType() );
}
}

return new RelationalSqmPredicate( RelationalPredicateOperator.GREATER_THAN_OR_EQUAL, lhs, rhs );
}

Expand All @@ -1312,18 +1289,6 @@ public Object visitLessThanPredicate(HqlParser.LessThanPredicateContext ctx) {
final SqmExpression lhs = (SqmExpression) ctx.expression().get( 0 ).accept( this );
final SqmExpression rhs = (SqmExpression) ctx.expression().get( 1 ).accept( this );

if ( lhs.getInferableType() != null ) {
if ( rhs instanceof InferableTypeSqmExpression ) {
( (InferableTypeSqmExpression) rhs ).impliedType( lhs.getInferableType() );
}
}

if ( rhs.getInferableType() != null ) {
if ( lhs instanceof InferableTypeSqmExpression ) {
( (InferableTypeSqmExpression) lhs ).impliedType( rhs.getInferableType() );
}
}

return new RelationalSqmPredicate( RelationalPredicateOperator.LESS_THAN, lhs, rhs );
}

Expand Down Expand Up @@ -2314,6 +2279,12 @@ public SqmExpression visitCountFunction(HqlParser.CountFunctionContext ctx) {
);
}

@Override
public Object visitLengthFunction(HqlParser.LengthFunctionContext ctx) {
final SqmExpression sqmExpression = (SqmExpression) ctx.expression().accept( this );
return new SqmLengthFunction( sqmExpression, resolveExpressableTypeBasic( Long.class ) );
}

@Override
public Object visitLocateFunction(HqlParser.LocateFunctionContext ctx) {
return super.visitLocateFunction( ctx );
Expand Down
Expand Up @@ -21,6 +21,7 @@
import org.hibernate.sql.ast.produce.metamodel.spi.BasicValuedExpressableType;
import org.hibernate.sql.ast.produce.spi.SqlSelectionExpression;
import org.hibernate.sql.ast.tree.spi.expression.SqlTuple;
import org.hibernate.sql.ast.tree.spi.expression.SubstrFunction;
import org.hibernate.sql.exec.spi.JdbcParameter;
import org.hibernate.sql.ast.tree.spi.QuerySpec;
import org.hibernate.sql.ast.tree.spi.expression.AbsFunction;
Expand Down Expand Up @@ -484,6 +485,23 @@ public void visitConcatFunction(ConcatFunction function) {
appendSql( CLOSE_PARENTHESIS );
}

@Override
@SuppressWarnings("unchecked")
public void visitSubstrFunction(SubstrFunction function) {
appendSql( "substr(" );

boolean firstPass = true;
for ( Expression expression : function.getExpressions() ) {
if ( ! firstPass ) {
appendSql( COMA_SEPARATOR );
}
expression.accept( this );
firstPass = false;
}

appendSql( CLOSE_PARENTHESIS );
}

@Override
@SuppressWarnings("unchecked")
public void visitCountFunction(CountFunction function) {
Expand Down Expand Up @@ -644,7 +662,7 @@ public void visitTrimFunction(TrimFunction function) {
@Override
@SuppressWarnings("unchecked")
public void visitUpperFunction(UpperFunction function) {
appendSql( "lower(" );
appendSql( "upper(" );
function.getArgument().accept( this );
appendSql( CLOSE_PARENTHESIS );
}
Expand Down
Expand Up @@ -40,6 +40,7 @@
import org.hibernate.sql.ast.tree.spi.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.spi.expression.SqlTuple;
import org.hibernate.sql.ast.tree.spi.expression.SqrtFunction;
import org.hibernate.sql.ast.tree.spi.expression.SubstrFunction;
import org.hibernate.sql.ast.tree.spi.expression.SumFunction;
import org.hibernate.sql.ast.tree.spi.expression.TrimFunction;
import org.hibernate.sql.ast.tree.spi.expression.UnaryOperation;
Expand Down Expand Up @@ -164,6 +165,8 @@ public interface SqlAstWalker {

void visitConcatFunction(ConcatFunction function);

void visitSubstrFunction(SubstrFunction function);

void visitCountFunction(CountFunction function);

void visitCountStarFunction(CountStarFunction function);
Expand Down
@@ -0,0 +1,74 @@
/*
* 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.sql.ast.tree.spi.expression;

import java.util.List;

import org.hibernate.sql.SqlExpressableType;
import org.hibernate.sql.ast.consume.spi.SqlAstWalker;
import org.hibernate.sql.ast.produce.spi.SqlExpressable;
import org.hibernate.sql.results.internal.SqlSelectionImpl;
import org.hibernate.sql.results.spi.SqlSelection;
import org.hibernate.type.descriptor.java.spi.BasicJavaDescriptor;
import org.hibernate.type.spi.TypeConfiguration;

/**
* @author Chris Cranford
*/
public class SubstrFunction implements StandardFunction {
private final String functionName;
private final List<Expression> expressions;
private final SqlExpressableType type;

public SubstrFunction(String name, List<Expression> expressions, SqlExpressableType type) {
this.functionName = name;
this.expressions = expressions;
this.type = type;
}

public String getFunctionName() {
return functionName;
}

public List<Expression> getExpressions() {
return expressions;
}

@Override
public SqlExpressableType getExpressableType() {
return type;
}

@Override
public SqlExpressableType getType() {
return getExpressableType();
}

@Override
public SqlExpressable getExpressable() {
return this;
}

@Override
public void accept(SqlAstWalker walker) {
walker.visitSubstrFunction( this );
}

@Override
public SqlSelection createSqlSelection(
int jdbcPosition,
int valuesArrayPosition,
BasicJavaDescriptor javaTypeDescriptor,
TypeConfiguration typeConfiguration) {
return new SqlSelectionImpl(
jdbcPosition,
valuesArrayPosition,
this,
getType().getJdbcValueExtractor()
);
}
}
Expand Up @@ -13,6 +13,7 @@
import java.sql.Types;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

import javax.persistence.EnumType;
import javax.persistence.TemporalType;
Expand All @@ -38,6 +39,7 @@
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.sql.SqlExpressableType;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.produce.metamodel.spi.BasicValuedExpressableType;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.StandardBasicTypes.StandardBasicType;
Expand Down Expand Up @@ -257,6 +259,14 @@ public MutabilityPlan<J> getMutabilityPlan() {
public BasicJavaDescriptor<J> getJavaTypeDescriptor() {
return javaTypeDescriptor;
}

@Override
public void visitJdbcTypes(
Consumer<SqlExpressableType> action,
Clause clause,
TypeConfiguration typeConfiguration) {
action.accept( getSqlExpressableType() );
}
}

public BasicTypeRegistry getBasicTypeRegistry() {
Expand Down
Expand Up @@ -48,6 +48,56 @@ public void testSubstrInsideConcat() {
} );
}

@Test
public void testLengthFunctionPredicate() {
sessionFactoryScope().inTransaction(
session -> {
List<Object> results = session.createQuery(
"select s.someString from SimpleEntity s where length(s.someString) > :p1 ORDER BY s.someString" )
.setParameter( "p1", 0L )
.list();
assertThat( results.size(), is( 2 ) );
assertThat( results.get( 0 ), is( "a" ) );
} );
}

@Test
public void testLengthFunctionSelection() {
sessionFactoryScope().inTransaction(
session -> {
List<Object> results = session.createQuery(
"select length(s.someString) from SimpleEntity s" )
.list();
assertThat( results.size(), is( 2 ) );
results.forEach( value -> assertThat( value, is( 1L ) ) );
} );
}

@Test
public void testSubstringFunctionPredicate() {
sessionFactoryScope().inTransaction(
session -> {
List<Object> results = session.createQuery(
"select s.someString from SimpleEntity s where substring(s.someString, 0, 1) = :p1" )
.setParameter( "p1", "a" )
.list();
assertThat( results.size(), is( 1 ) );
assertThat( results.get( 0 ), is( "a" ) );
} );
}

@Test
public void testSubstringFunctionSelection() {
sessionFactoryScope().inTransaction(
session -> {
List<Object> results = session.createQuery(
"select substring(s.someString, 0, 1) from SimpleEntity s ORDER BY s.someString" )
.list();
assertThat( results.size(), is( 2 ) );
assertThat( results.get( 0 ), is( "a" ) );
} );
}

@Test
public void testSelectAnIntegerConstant() {
sessionFactoryScope().inTransaction(
Expand Down

0 comments on commit 65b4782

Please sign in to comment.