Fix CriteriaBuidler#locate() arguments order rendering

This commit is contained in:
Andrea Boriero 2022-01-17 12:06:56 +01:00 committed by Andrea Boriero
parent 23a011385d
commit 9e7a091d67
15 changed files with 215 additions and 48 deletions

View File

@ -170,10 +170,10 @@ public class CacheDialect extends Dialect {
queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern( queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern(
"locate", "locate",
queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ), queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ),
"$find(?2,?1)", "$find(?1,?2)",
"$find(?2,?1,?3)", "$find(?1,?2,?3)",
STRING, STRING, INTEGER STRING, STRING, INTEGER
).setArgumentListSignature("(pattern, string[, start])"); ).setArgumentListSignature("(string, pattern[, start])");
CommonFunctionFactory.bitLength_pattern( queryEngine, "($length(?1)*8)" ); CommonFunctionFactory.bitLength_pattern( queryEngine, "($length(?1)*8)" );
useJdbcEscape(queryEngine, "sin"); useJdbcEscape(queryEngine, "sin");

View File

@ -276,15 +276,15 @@ public class FirebirdDialect extends Dialect {
CommonFunctionFactory.reverse( queryEngine ); CommonFunctionFactory.reverse( queryEngine );
CommonFunctionFactory.bitandorxornot_binAndOrXorNot( queryEngine ); CommonFunctionFactory.bitandorxornot_binAndOrXorNot( queryEngine );
CommonFunctionFactory.leastGreatest_minMaxValue( queryEngine ); CommonFunctionFactory.leastGreatest_minMaxValue( queryEngine );
SqmFunctionRegistry functionRegistry = queryEngine.getSqmFunctionRegistry(); SqmFunctionRegistry functionRegistry = queryEngine.getSqmFunctionRegistry();
functionRegistry.registerBinaryTernaryPattern( functionRegistry.registerBinaryTernaryPattern(
"locate", "locate",
integerType, integerType,
"position(?1 in ?2)", "position(?2 in ?1)",
"position(?1,?2,?3)", "position(?2,?1,?3)",
STRING, STRING, INTEGER STRING, STRING, INTEGER
).setArgumentListSignature( "(pattern, string[, start])" ); ).setArgumentListSignature( "(string, pattern[, start])" );
functionRegistry.namedDescriptorBuilder( "ascii_val" ) functionRegistry.namedDescriptorBuilder( "ascii_val" )
.setExactArgumentCount( 1 ) .setExactArgumentCount( 1 )
.setInvariantType( shortType ) .setInvariantType( shortType )

View File

@ -185,10 +185,10 @@ public class InformixDialect extends Dialect {
queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern( queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern(
"locate", "locate",
queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ), queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ),
"instr(?2,?1)", "instr(?1,?2)",
"instr(?2,?1,?3)", "instr(?1,?2,?3)",
STRING, STRING, INTEGER STRING, STRING, INTEGER
).setArgumentListSignature("(pattern, string[, start])"); ).setArgumentListSignature("(string, pattern[, start])");
//coalesce() and nullif() both supported since Informix 12 //coalesce() and nullif() both supported since Informix 12

View File

@ -279,16 +279,10 @@ public class IngresDialect extends Dialect {
CommonFunctionFactory.format_dateFormat( queryEngine ); CommonFunctionFactory.format_dateFormat( queryEngine );
CommonFunctionFactory.dateTrunc( queryEngine ); CommonFunctionFactory.dateTrunc( queryEngine );
CommonFunctionFactory.bitLength_pattern( queryEngine, "octet_length(hex(?1))*4" ); CommonFunctionFactory.bitLength_pattern( queryEngine, "octet_length(hex(?1))*4" );
CommonFunctionFactory.locate_positionSubstring( queryEngine );
final BasicType<Integer> integerType = queryEngine.getTypeConfiguration().getBasicTypeRegistry() final BasicType<Integer> integerType = queryEngine.getTypeConfiguration().getBasicTypeRegistry()
.resolve( StandardBasicTypes.INTEGER ); .resolve( StandardBasicTypes.INTEGER );
queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern(
"locate",
integerType,
"position(?1 in ?2)",
"(position(?1 in substring(?2 from ?3))+(?3)-1)",
STRING, STRING, INTEGER
).setArgumentListSignature("(pattern, string[, start])");
queryEngine.getSqmFunctionRegistry().registerPattern( "extract", "date_part('?1',?2)", integerType ); queryEngine.getSqmFunctionRegistry().registerPattern( "extract", "date_part('?1',?2)", integerType );

View File

@ -166,9 +166,9 @@ public class MaxDBDialect extends Dialect {
queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern( queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern(
"locate", "locate",
integerType, "index(?2,?1)", "index(?2,?1,?3)", integerType, "index(?1,?2)", "index(?1,?2,?3)",
STRING, STRING, INTEGER STRING, STRING, INTEGER
).setArgumentListSignature("(pattern, string[, start])"); ).setArgumentListSignature("(string, pattern[, start])");
} }
@Override @Override

View File

@ -274,10 +274,10 @@ public class SQLiteDialect extends Dialect {
queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern( queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern(
"locate", "locate",
integerType, integerType,
"instr(?2,?1)", "instr(?1,?2)",
"instr(?2,?1,?3)", "instr(?1,?2,?3)",
STRING, STRING, INTEGER STRING, STRING, INTEGER
).setArgumentListSignature("(pattern, string[, start])"); ).setArgumentListSignature("(string, pattern[, start])");
queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern( queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern(
"lpad", "lpad",
stringType, stringType,

View File

@ -170,10 +170,10 @@ public class TimesTenDialect extends Dialect {
queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern( queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern(
"locate", "locate",
queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ), queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ),
"instr(?2,?1)", "instr(?1,?2)",
"instr(?2,?1,?3)", "instr(?1,?2,?3)",
STRING, STRING, INTEGER STRING, STRING, INTEGER
).setArgumentListSignature("(pattern, string[, start])"); ).setArgumentListSignature("(string, pattern[, start])");
} }
@Override @Override

View File

@ -277,10 +277,10 @@ public abstract class AbstractHANADialect extends Dialect {
queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern( queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern(
"locate", "locate",
queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ), queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ),
"locate(?2,?1)", "locate(?1,?2)",
"locate(?2,?1,?3)", "locate(?1,?2,?3)",
FunctionParameterType.STRING, FunctionParameterType.STRING, FunctionParameterType.INTEGER FunctionParameterType.STRING, FunctionParameterType.STRING, FunctionParameterType.INTEGER
).setArgumentListSignature("(pattern, string[, start])"); ).setArgumentListSignature("(string, pattern[, start])");
CommonFunctionFactory.ceiling_ceil( queryEngine ); CommonFunctionFactory.ceiling_ceil( queryEngine );
CommonFunctionFactory.concat_pipeOperator( queryEngine ); CommonFunctionFactory.concat_pipeOperator( queryEngine );

View File

@ -184,10 +184,10 @@ public class OracleDialect extends Dialect {
queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern( queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern(
"locate", "locate",
queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ), queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ),
"instr(?2,?1)", "instr(?1,?2)",
"instr(?2,?1,?3)", "instr(?1,?2,?3)",
FunctionParameterType.STRING, FunctionParameterType.STRING, FunctionParameterType.INTEGER FunctionParameterType.STRING, FunctionParameterType.STRING, FunctionParameterType.INTEGER
).setArgumentListSignature("(pattern, string[, start])"); ).setArgumentListSignature("(string, pattern[, start])");
} }
@Override @Override

View File

@ -452,10 +452,10 @@ public class PostgreSQLDialect extends Dialect {
queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern( queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern(
"locate", "locate",
queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ), queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ),
"position(?1 in ?2)", "position(?2 in ?1)",
"(position(?1 in substring(?2 from ?3))+(?3)-1)", "(position(?2 in substring(?1 from ?3))+(?3)-1)",
FunctionParameterType.STRING, FunctionParameterType.STRING, FunctionParameterType.INTEGER FunctionParameterType.STRING, FunctionParameterType.STRING, FunctionParameterType.INTEGER
).setArgumentListSignature("(pattern, string[, start])"); ).setArgumentListSignature("(string, pattern[, start])");
if ( getVersion().isSameOrAfter( 9, 4 ) ) { if ( getVersion().isSameOrAfter( 9, 4 ) ) {
CommonFunctionFactory.makeDateTimeTimestamp( queryEngine ); CommonFunctionFactory.makeDateTimeTimestamp( queryEngine );

View File

@ -1548,20 +1548,32 @@ public class CommonFunctionFactory {
} }
public static void locate(QueryEngine queryEngine) { public static void locate(QueryEngine queryEngine) {
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "locate" ) queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern(
.setInvariantType( "locate",
queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ) queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ),
"locate(?2,?1)",
"locate(?2,?1,?3)",
STRING,
STRING,
INTEGER
) )
.setArgumentCountBetween( 2, 3 ) .setArgumentListSignature( "(STRING string, STRING pattern[, INTEGER start])" );
.setParameterTypes(STRING, STRING, INTEGER)
.setArgumentListSignature( "(STRING pattern, STRING string[, INTEGER start])" )
.register();
} }
/** /**
* Transact SQL-style * Transact SQL-style
*/ */
public static void locate_charindex(QueryEngine queryEngine) { public static void locate_charindex(QueryEngine queryEngine) {
queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern(
"locate",
queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ),
"charindex(?2,?1)",
"charindex(?2,?1,?3)",
STRING,
STRING,
INTEGER
).setArgumentListSignature( "(STRING string, STRING pattern[, INTEGER start])" );
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "charindex" ) queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "charindex" )
.setInvariantType( .setInvariantType(
queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ) queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER )
@ -1570,7 +1582,6 @@ public class CommonFunctionFactory {
.setParameterTypes( STRING, STRING, INTEGER ) .setParameterTypes( STRING, STRING, INTEGER )
.setArgumentListSignature( "(STRING pattern, STRING string[, INTEGER start])" ) .setArgumentListSignature( "(STRING pattern, STRING string[, INTEGER start])" )
.register(); .register();
queryEngine.getSqmFunctionRegistry().registerAlternateKey( "locate", "charindex" );
} }
/** /**
@ -1580,10 +1591,10 @@ public class CommonFunctionFactory {
queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern( queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern(
"locate", "locate",
queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ), queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ),
"position(?1 in ?2)", "(position(?1 in substring(?2 from ?3))+?3)", "position(?2 in ?1)", "(position(?2 in substring(?1 from ?3))+?3)",
STRING, STRING, INTEGER STRING, STRING, INTEGER
) )
.setArgumentListSignature( "(STRING pattern, STRING string[, INTEGER start])" ); .setArgumentListSignature( "(STRING string, STRING pattern[, INTEGER start])" );
} }
/** /**
* ANSI-style substring * ANSI-style substring

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.dialect.function; package org.hibernate.dialect.function;
import org.hibernate.mapping.Collection;
import org.hibernate.query.ReturnableType; import org.hibernate.query.ReturnableType;
import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.function.AbstractSqmFunctionDescriptor; import org.hibernate.query.sqm.function.AbstractSqmFunctionDescriptor;
@ -17,6 +18,8 @@ import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING; import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING;
@ -42,6 +45,18 @@ public class LocatePositionEmulation extends AbstractSqmFunctionDescriptor {
ReturnableType<T> impliedResultType, ReturnableType<T> impliedResultType,
QueryEngine queryEngine, QueryEngine queryEngine,
TypeConfiguration typeConfiguration) { TypeConfiguration typeConfiguration) {
if ( arguments instanceof ArrayList ) {
Collections.swap( arguments, 0, 1 );
}
else {
List<SqmTypedNode<?>> argumentTemp = new ArrayList<>( arguments.size() );
argumentTemp.add( arguments.get( 1 ) );
argumentTemp.add( arguments.get( 0 ) );
for ( int i = 2; i < arguments.size(); i++ ) {
argumentTemp.add( arguments.get( i ) );
}
arguments = argumentTemp;
}
return queryEngine.getSqmFunctionRegistry().findFunctionDescriptor( "locate" ) return queryEngine.getSqmFunctionRegistry().findFunctionDescriptor( "locate" )
.generateSqmExpression( arguments, impliedResultType, queryEngine, typeConfiguration ); .generateSqmExpression( arguments, impliedResultType, queryEngine, typeConfiguration );
} }

View File

@ -1,3 +1,9 @@
/*
* 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.jpa.compliance; package org.hibernate.orm.test.jpa.compliance;
import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.AvailableSettings;

View File

@ -0,0 +1,141 @@
/*
* 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.jpa.compliance;
import java.util.List;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Root;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@Jpa(
annotatedClasses = CriteriaLocateTest.Person.class
)
public class CriteriaLocateTest {
@BeforeEach
public void setUp(EntityManagerFactoryScope scope) {
scope.inTransaction(
entityManager -> {
for ( int i = 0; i < 10; i++ ) {
Person person;
if ( i == 3 ) {
person = new Person( i, "Andrea" );
}
else if ( i == 4 ) {
person = new Person( i, "Andrew" );
}
else {
person = new Person( i, "Luigi " + i );
}
entityManager.persist( person );
}
}
);
}
@AfterEach
public void tearDown(EntityManagerFactoryScope scope){
scope.inTransaction(
entityManager ->
entityManager.createQuery( "delete from Person" ).executeUpdate()
);
}
@Test
public void testLocate(EntityManagerFactoryScope scope) {
scope.inEntityManager(
entityManager -> {
final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
final CriteriaQuery<Integer> query = criteriaBuilder.createQuery( Integer.class );
final Root<Person> personRoot = query.from( Person.class );
query.select( personRoot.get( "id" ) );
final Expression<Integer> locate = criteriaBuilder.locate(
personRoot.get( "name" ),
criteriaBuilder.literal( "nd" )
);
query.where( criteriaBuilder.equal( locate, 2 ) );
final List<Integer> results = entityManager.createQuery( query ).getResultList();
assertEquals( 2, results.size() );
assertTrue( results.contains( 3 ) );
assertTrue( results.contains( 4 ) );
}
);
}
@Test
public void testLocate2(EntityManagerFactoryScope scope) {
scope.inEntityManager(
entityManager -> {
final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
final CriteriaQuery<Integer> query = criteriaBuilder.createQuery( Integer.class );
final Root<Person> personRoot = query.from( Person.class );
query.select( personRoot.get( "id" ) );
final Expression<Integer> locate = criteriaBuilder.locate(
personRoot.get( "name" ),
"nd" ,
1
);
query.where( criteriaBuilder.equal( locate, 2 ) );
final List<Integer> results = entityManager.createQuery( query ).getResultList();
assertEquals( 2, results.size() );
assertTrue( results.contains( 3 ) );
assertTrue( results.contains( 4 ) );
}
);
}
@Entity(name = "Person")
@Table(name = "PERSON_TABLE")
public static class Person {
@Id
private Integer id;
private String name;
Person() {
}
public Person(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
}
}

View File

@ -407,11 +407,11 @@ public class FunctionTests {
public void testLocateFunction(SessionFactoryScope scope) { public void testLocateFunction(SessionFactoryScope scope) {
scope.inTransaction( scope.inTransaction(
session -> { session -> {
session.createQuery("select locate('hello', e.theString) from EntityOfBasics e") session.createQuery("select locate( e.theString, 'hello') from EntityOfBasics e")
.list(); .list();
session.createQuery("select locate('hello', e.theString, e.theInteger) from EntityOfBasics e") session.createQuery("select locate( e.theString, 'hello', e.theInteger) from EntityOfBasics e")
.list(); .list();
assertThat( session.createQuery("select locate('world', 'hello world')").getSingleResult(), is(7) ); assertThat( session.createQuery("select locate( 'hello world', 'world')").getSingleResult(), is(7) );
} }
); );
} }