HHH-15162 Add STRING_OR_CLOB FunctionParameterType and use that for various length functions
This commit is contained in:
parent
9d3726e39d
commit
6f0ec52e81
|
@ -366,6 +366,7 @@ public abstract class AbstractHANADialect extends Dialect {
|
|||
functionFactory.format_toVarchar();
|
||||
functionFactory.currentUtcdatetimetimestamp();
|
||||
functionFactory.everyAny_minMaxCase();
|
||||
functionFactory.octetLength_pattern( "length(to_binary(?1))" );
|
||||
functionFactory.bitLength_pattern( "length(to_binary(?1))*8" );
|
||||
|
||||
functionFactory.windowFunctions();
|
||||
|
|
|
@ -284,6 +284,7 @@ public class DerbyDialect extends Dialect {
|
|||
functionFactory.characterLength_length( SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
|
||||
functionFactory.power_expLn();
|
||||
functionFactory.round_floor();
|
||||
functionFactory.octetLength_pattern( "length(?1)" );
|
||||
functionFactory.bitLength_pattern( "length(?1)*8" );
|
||||
|
||||
queryEngine.getSqmFunctionRegistry().register(
|
||||
|
|
|
@ -61,7 +61,6 @@ import org.hibernate.dialect.temptable.TemporaryTableKind;
|
|||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
import org.hibernate.service.ServiceRegistry;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||
|
@ -163,7 +162,6 @@ public class OracleDialect extends Dialect {
|
|||
functionFactory.rownumRowid();
|
||||
functionFactory.sysdate();
|
||||
functionFactory.systimestamp();
|
||||
functionFactory.characterLength_length( SqlAstNodeRenderingMode.DEFAULT );
|
||||
functionFactory.addMonths();
|
||||
functionFactory.monthsBetween();
|
||||
functionFactory.everyAny_minMaxCase();
|
||||
|
@ -176,7 +174,10 @@ public class OracleDialect extends Dialect {
|
|||
functionFactory.covarPopSamp();
|
||||
functionFactory.corr();
|
||||
functionFactory.regrLinearRegressionAggregates();
|
||||
functionFactory.bitLength_pattern( "vsize(?1)*8" );
|
||||
functionFactory.characterLength_length( "dbms_lob.getlength(?1)" );
|
||||
// Approximate octet and bit length for clobs since exact size determination would require a custom function
|
||||
functionFactory.octetLength_pattern( "lengthb(?1)", "dbms_lob.getlength(?1)*2" );
|
||||
functionFactory.bitLength_pattern( "lengthb(?1)*8", "dbms_lob.getlength(?1)*16" );
|
||||
|
||||
if ( getVersion().isBefore( 9 ) ) {
|
||||
queryEngine.getSqmFunctionRegistry().register( "coalesce", new NvlCoalesceEmulation() );
|
||||
|
|
|
@ -441,7 +441,6 @@ public class PostgreSQLDialect extends Dialect {
|
|||
}
|
||||
functionFactory.cbrt();
|
||||
functionFactory.trim2();
|
||||
functionFactory.octetLength();
|
||||
functionFactory.repeat();
|
||||
functionFactory.md5();
|
||||
functionFactory.initcap();
|
||||
|
@ -450,11 +449,12 @@ public class PostgreSQLDialect extends Dialect {
|
|||
//also natively supports ANSI-style substring()
|
||||
functionFactory.translate();
|
||||
functionFactory.toCharNumberDateTimestamp();
|
||||
functionFactory.concat_pipeOperator();
|
||||
functionFactory.concat_pipeOperator( "convert_from(lo_get(?1),pg_client_encoding())" );
|
||||
functionFactory.localtimeLocaltimestamp();
|
||||
functionFactory.dateTrunc();
|
||||
functionFactory.bitLength();
|
||||
functionFactory.octetLength();
|
||||
functionFactory.length_characterLength_pattern( "length(lo_get(?1),pg_client_encoding())" );
|
||||
functionFactory.bitLength_pattern( "bit_length(?1)", "length(lo_get(?1))*8" );
|
||||
functionFactory.octetLength_pattern( "octet_length(?1)", "length(lo_get(?1))" );
|
||||
functionFactory.ascii();
|
||||
functionFactory.char_chr();
|
||||
functionFactory.position();
|
||||
|
|
|
@ -241,7 +241,8 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
|
||||
functionFactory.truncate_round();
|
||||
functionFactory.everyAny_minMaxIif();
|
||||
functionFactory.bitLength_pattern( "datalength(?1) * 8" );
|
||||
functionFactory.octetLength_pattern( "datalength(?1)" );
|
||||
functionFactory.bitLength_pattern( "datalength(?1)*8" );
|
||||
|
||||
if ( getVersion().isSameOrAfter( 10 ) ) {
|
||||
functionFactory.locate_charindex();
|
||||
|
|
|
@ -223,7 +223,8 @@ public class SybaseDialect extends AbstractTransactSQLDialect {
|
|||
|
||||
functionFactory.replace_strReplace();
|
||||
functionFactory.everyAny_minMaxCase();
|
||||
functionFactory.bitLength_pattern( "datalength(?1) * 8" );
|
||||
functionFactory.octetLength_pattern( "datalength(?1)" );
|
||||
functionFactory.bitLength_pattern( "datalength(?1)*8" );
|
||||
|
||||
queryEngine.getSqmFunctionRegistry().register( "timestampadd",
|
||||
new IntegralTimestampaddFunction( this, queryEngine.getTypeConfiguration() ) );
|
||||
|
|
|
@ -13,9 +13,6 @@ import org.hibernate.dialect.Dialect;
|
|||
|
||||
import org.hibernate.query.spi.QueryEngine;
|
||||
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
|
||||
import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator;
|
||||
import org.hibernate.query.sqm.produce.function.ArgumentsValidator;
|
||||
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
|
||||
import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.type.BasicType;
|
||||
|
@ -1114,6 +1111,10 @@ public class CommonFunctionFactory {
|
|||
.register();
|
||||
}
|
||||
|
||||
public void concat_pipeOperator(String clobPattern) {
|
||||
functionRegistry.register( "concat", new ConcatPipeFunction( clobPattern, typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Oracle-style
|
||||
*/
|
||||
|
@ -1350,11 +1351,19 @@ public class CommonFunctionFactory {
|
|||
functionRegistry.namedDescriptorBuilder( "character_length" )
|
||||
.setInvariantType(integerType)
|
||||
.setExactArgumentCount( 1 )
|
||||
.setParameterTypes(STRING)
|
||||
.setParameterTypes(STRING_OR_CLOB)
|
||||
.register();
|
||||
functionRegistry.registerAlternateKey( "length", "character_length" );
|
||||
}
|
||||
|
||||
public void length_characterLength_pattern(String clobPattern) {
|
||||
functionRegistry.register(
|
||||
"character_length",
|
||||
new LengthFunction( "character_length", "character_length(?1)", clobPattern, typeConfiguration )
|
||||
);
|
||||
functionRegistry.registerAlternateKey( "character_length", "length" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Transact SQL-style
|
||||
*/
|
||||
|
@ -1362,7 +1371,7 @@ public class CommonFunctionFactory {
|
|||
functionRegistry.namedDescriptorBuilder( "len" )
|
||||
.setInvariantType(integerType)
|
||||
.setExactArgumentCount( 1 )
|
||||
.setParameterTypes(STRING)
|
||||
.setParameterTypes(STRING_OR_CLOB)
|
||||
.register();
|
||||
functionRegistry.registerAlternateKey( "character_length", "len" );
|
||||
functionRegistry.registerAlternateKey( "length", "len" );
|
||||
|
@ -1375,25 +1384,48 @@ public class CommonFunctionFactory {
|
|||
functionRegistry.namedDescriptorBuilder( "length" )
|
||||
.setInvariantType(integerType)
|
||||
.setExactArgumentCount( 1 )
|
||||
.setParameterTypes(STRING)
|
||||
.setParameterTypes(STRING_OR_CLOB)
|
||||
.setArgumentRenderingMode( argumentRenderingMode )
|
||||
.register();
|
||||
functionRegistry.registerAlternateKey( "character_length", "length" );
|
||||
}
|
||||
|
||||
public void characterLength_length(String clobPattern) {
|
||||
functionRegistry.register(
|
||||
"length",
|
||||
new LengthFunction( "length", "length(?1)", clobPattern, typeConfiguration )
|
||||
);
|
||||
functionRegistry.registerAlternateKey( "character_length", "length" );
|
||||
}
|
||||
|
||||
public void octetLength() {
|
||||
functionRegistry.namedDescriptorBuilder( "octet_length" )
|
||||
.setInvariantType(integerType)
|
||||
.setExactArgumentCount( 1 )
|
||||
.setParameterTypes(STRING)
|
||||
.setParameterTypes(STRING_OR_CLOB)
|
||||
.register();
|
||||
}
|
||||
|
||||
public void octetLength_pattern(String pattern) {
|
||||
functionRegistry.patternDescriptorBuilder( "octet_length", pattern )
|
||||
.setInvariantType(integerType)
|
||||
.setExactArgumentCount( 1 )
|
||||
.setParameterTypes(STRING_OR_CLOB)
|
||||
.register();
|
||||
}
|
||||
|
||||
public void octetLength_pattern(String pattern, String clobPattern) {
|
||||
functionRegistry.register(
|
||||
"octet_length",
|
||||
new LengthFunction( "octet_length", pattern, clobPattern, typeConfiguration )
|
||||
);
|
||||
}
|
||||
|
||||
public void bitLength() {
|
||||
functionRegistry.namedDescriptorBuilder( "bit_length" )
|
||||
.setInvariantType(integerType)
|
||||
.setExactArgumentCount( 1 )
|
||||
.setParameterTypes(STRING)
|
||||
.setParameterTypes(STRING_OR_CLOB)
|
||||
.register();
|
||||
}
|
||||
|
||||
|
@ -1401,10 +1433,17 @@ public class CommonFunctionFactory {
|
|||
functionRegistry.patternDescriptorBuilder( "bit_length", pattern )
|
||||
.setInvariantType(integerType)
|
||||
.setExactArgumentCount( 1 )
|
||||
.setParameterTypes(STRING)
|
||||
.setParameterTypes(STRING_OR_CLOB)
|
||||
.register();
|
||||
}
|
||||
|
||||
public void bitLength_pattern(String pattern, String clobPattern) {
|
||||
functionRegistry.register(
|
||||
"bit_length",
|
||||
new LengthFunction( "bit_length", pattern, clobPattern, typeConfiguration )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* ANSI-style
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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.dialect.function;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
||||
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
|
||||
import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers;
|
||||
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
|
||||
import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
|
||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING;
|
||||
|
||||
/**
|
||||
* A concat function with a pattern for clob arguments.
|
||||
*/
|
||||
public class ConcatPipeFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||
|
||||
private final PatternRenderer clobPatternRenderer;
|
||||
|
||||
public ConcatPipeFunction(String clobPattern, TypeConfiguration typeConfiguration) {
|
||||
super(
|
||||
"concat",
|
||||
StandardArgumentsValidators.min( 1 ),
|
||||
StandardFunctionReturnTypeResolvers.invariant(
|
||||
typeConfiguration.getBasicTypeRegistry().resolve( StandardBasicTypes.STRING )
|
||||
),
|
||||
StandardFunctionArgumentTypeResolvers.impliedOrInvariant( typeConfiguration, STRING )
|
||||
);
|
||||
this.clobPatternRenderer = new PatternRenderer( clobPattern );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(
|
||||
SqlAppender sqlAppender,
|
||||
List<? extends SqlAstNode> sqlAstArguments,
|
||||
SqlAstTranslator<?> walker) {
|
||||
String separator = "(";
|
||||
for ( int i = 0; i < sqlAstArguments.size(); i++ ) {
|
||||
final Expression expression = (Expression) sqlAstArguments.get( i );
|
||||
final JdbcType jdbcType = expression.getExpressionType().getJdbcMappings().get( 0 ).getJdbcType();
|
||||
sqlAppender.appendSql( separator );
|
||||
switch ( jdbcType.getJdbcTypeCode() ) {
|
||||
case SqlTypes.CLOB:
|
||||
case SqlTypes.NCLOB:
|
||||
clobPatternRenderer.render( sqlAppender, Collections.singletonList( expression ), walker );
|
||||
break;
|
||||
default:
|
||||
expression.accept( walker );
|
||||
break;
|
||||
}
|
||||
separator = "||";
|
||||
}
|
||||
sqlAppender.appendSql( ')' );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSignature(String name) {
|
||||
return "(STRING string0[, STRING string1[, ...]])";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.dialect.function;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
||||
import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator;
|
||||
import org.hibernate.query.sqm.produce.function.FunctionParameterType;
|
||||
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
|
||||
import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers;
|
||||
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
|
||||
import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
|
||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING;
|
||||
|
||||
/**
|
||||
* A length function with separate patterns for string and clob argument.
|
||||
*/
|
||||
public class LengthFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||
|
||||
private final PatternRenderer stringPatternRenderer;
|
||||
private final PatternRenderer clobPatternRenderer;
|
||||
|
||||
public LengthFunction(String functionName, String stringPattern, String clobPattern, TypeConfiguration typeConfiguration) {
|
||||
super(
|
||||
functionName,
|
||||
new ArgumentTypesValidator(
|
||||
StandardArgumentsValidators.exactly( 1 ),
|
||||
FunctionParameterType.STRING_OR_CLOB
|
||||
),
|
||||
StandardFunctionReturnTypeResolvers.invariant(
|
||||
typeConfiguration.getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER )
|
||||
),
|
||||
StandardFunctionArgumentTypeResolvers.invariant( typeConfiguration, STRING )
|
||||
);
|
||||
this.stringPatternRenderer = new PatternRenderer( stringPattern );
|
||||
this.clobPatternRenderer = new PatternRenderer( clobPattern );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(
|
||||
SqlAppender sqlAppender,
|
||||
List<? extends SqlAstNode> sqlAstArguments,
|
||||
SqlAstTranslator<?> walker) {
|
||||
final Expression expression = (Expression) sqlAstArguments.get( 0 );
|
||||
final JdbcType jdbcType = expression.getExpressionType().getJdbcMappings().get( 0 ).getJdbcType();
|
||||
switch ( jdbcType.getJdbcTypeCode() ) {
|
||||
case SqlTypes.CLOB:
|
||||
case SqlTypes.NCLOB:
|
||||
clobPatternRenderer.render( sqlAppender, sqlAstArguments, walker );
|
||||
break;
|
||||
default:
|
||||
stringPatternRenderer.render( sqlAppender, sqlAstArguments, walker );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -163,6 +163,11 @@ public class ArgumentTypesValidator implements ArgumentsValidator {
|
|||
throwError(type, javaType, functionName, count);
|
||||
}
|
||||
break;
|
||||
case STRING_OR_CLOB:
|
||||
if ( !isCharacterOrClobType(code) ) {
|
||||
throwError(type, javaType, functionName, count);
|
||||
}
|
||||
break;
|
||||
case NUMERIC:
|
||||
if ( !isNumericType(code) ) {
|
||||
throwError(type, javaType, functionName, count);
|
||||
|
|
|
@ -78,5 +78,9 @@ public enum FunctionParameterType {
|
|||
/**
|
||||
* Any type with an order (numeric, string, and temporal types)
|
||||
*/
|
||||
COMPARABLE
|
||||
COMPARABLE,
|
||||
/**
|
||||
* @see org.hibernate.type.SqlTypes#isCharacterOrClobType(int)
|
||||
*/
|
||||
STRING_OR_CLOB
|
||||
}
|
||||
|
|
|
@ -142,6 +142,7 @@ public final class StandardFunctionArgumentTypeResolvers {
|
|||
FunctionParameterType type) {
|
||||
switch ( type ) {
|
||||
case STRING:
|
||||
case STRING_OR_CLOB:
|
||||
return typeConfiguration.getBasicTypeForJavaType( String.class );
|
||||
case NUMERIC:
|
||||
return typeConfiguration.getBasicTypeForJavaType( BigDecimal.class );
|
||||
|
|
|
@ -483,6 +483,27 @@ public class SqlTypes {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the given JDBC type code represent some sort of
|
||||
* character string type?
|
||||
* @param sqlType a JDBC type code from {@link Types}
|
||||
*/
|
||||
public static boolean isCharacterOrClobType(int sqlType) {
|
||||
switch (sqlType) {
|
||||
case Types.CHAR:
|
||||
case Types.VARCHAR:
|
||||
case Types.LONGVARCHAR:
|
||||
case Types.NCHAR:
|
||||
case Types.NVARCHAR:
|
||||
case Types.LONGNVARCHAR:
|
||||
case Types.CLOB:
|
||||
case Types.NCLOB:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the given JDBC type code represent some sort of
|
||||
* character string type?
|
||||
|
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* 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.lob;
|
||||
|
||||
import java.sql.Clob;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.query.Query;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Lob;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.Tuple;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.isOneOf;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
|
||||
@TestForIssue(jiraKey = "HHH-15162")
|
||||
@DomainModel(
|
||||
annotatedClasses = LobStringFunctionsTest.TestEntity.class
|
||||
)
|
||||
@SessionFactory
|
||||
public class LobStringFunctionsTest {
|
||||
|
||||
private static final int LONG_STRING_SIZE = 3999;
|
||||
|
||||
private final String value1 = buildRecursively( LONG_STRING_SIZE, 'x' );
|
||||
private final String value2 = buildRecursively( LONG_STRING_SIZE, 'y' );
|
||||
|
||||
@BeforeEach
|
||||
protected void prepareTest(SessionFactoryScope scope) throws Exception {
|
||||
TestEntity entity = new TestEntity();
|
||||
scope.inTransaction( session -> {
|
||||
|
||||
entity.setFirstLobField( value1 );
|
||||
entity.setSecondLobField( value2 );
|
||||
entity.setClobField( session.getLobHelper().createClob( value2 ) );
|
||||
session.save( entity );
|
||||
} );
|
||||
|
||||
scope.inTransaction( session -> {
|
||||
final TestEntity testEntity = session.find( TestEntity.class, entity.getId() );
|
||||
assertThat( testEntity.getFirstLobField(), is( value1 ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session ->
|
||||
session.createQuery( "delete from TestEntity" ).executeUpdate()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLengthFunction(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
final Query<Tuple> query = session.createQuery(
|
||||
"select length(e.firstLobField),length(e.secondLobField),length(e.clobField) from TestEntity e",
|
||||
Tuple.class
|
||||
);
|
||||
|
||||
final List<Tuple> results = query.getResultList();
|
||||
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
|
||||
final Tuple testEntity = results.get( 0 );
|
||||
assertThat( testEntity.get( 0, Integer.class ), is( value1.length() ) );
|
||||
assertThat( testEntity.get( 1, Integer.class ), is( value2.length() ) );
|
||||
assertThat( testEntity.get( 2, Integer.class ), is( value2.length() ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOctetLengthFunction(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
final Query<Tuple> query = session.createQuery(
|
||||
"select octet_length(e.firstLobField),octet_length(e.secondLobField),octet_length(e.clobField) from TestEntity e",
|
||||
Tuple.class
|
||||
);
|
||||
|
||||
final List<Tuple> results = query.getResultList();
|
||||
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
|
||||
final Tuple testEntity = results.get( 0 );
|
||||
// Some databases report 2 octets per character
|
||||
assertThat( testEntity.get( 0, Integer.class ), isOneOf( value1.length(), value1.length() * 2 ) );
|
||||
assertThat( testEntity.get( 1, Integer.class ), isOneOf( value2.length(), value2.length() * 2 ) );
|
||||
assertThat( testEntity.get( 2, Integer.class ), isOneOf( value2.length(), value2.length() * 2 ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBitLengthFunction(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
final Query<Tuple> query = session.createQuery(
|
||||
"select bit_length(e.firstLobField),bit_length(e.secondLobField),bit_length(e.clobField) from TestEntity e",
|
||||
Tuple.class
|
||||
);
|
||||
|
||||
final List<Tuple> results = query.getResultList();
|
||||
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
|
||||
final Tuple testEntity = results.get( 0 );
|
||||
// Some databases report 16 bit per character
|
||||
assertThat( testEntity.get( 0, Integer.class ), isOneOf( value1.length() * 8, value1.length() * 16 ) );
|
||||
assertThat( testEntity.get( 1, Integer.class ), isOneOf( value2.length() * 8, value2.length() * 16 ) );
|
||||
assertThat( testEntity.get( 2, Integer.class ), isOneOf( value2.length() * 8, value2.length() * 16 ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcatFunction(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
// Use trim('') instead of '' since Sybase interprets that as single space string...
|
||||
final Query<Tuple> query = session.createQuery(
|
||||
"select concat(e.firstLobField, trim('')),concat(e.secondLobField, trim('')),concat(e.clobField, trim('')) from TestEntity e",
|
||||
Tuple.class
|
||||
);
|
||||
|
||||
final List<Tuple> results = query.getResultList();
|
||||
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
|
||||
final Tuple testEntity = results.get( 0 );
|
||||
assertThat( testEntity.get( 0, String.class ), is( value1 ) );
|
||||
assertThat( testEntity.get( 1, String.class ), is( value2 ) );
|
||||
assertThat( testEntity.get( 2, String.class ), is( value2 ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity(name = "TestEntity")
|
||||
@Table(name = "TEST_ENTITY")
|
||||
public static class TestEntity {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
@Lob
|
||||
@Column(length = LONG_STRING_SIZE) //needed by HSQLDialect
|
||||
String firstLobField;
|
||||
|
||||
@Lob
|
||||
@Column(length = LONG_STRING_SIZE) //needed by HSQLDialect
|
||||
String secondLobField;
|
||||
|
||||
@Lob
|
||||
@Column(length = LONG_STRING_SIZE) //needed by HSQLDialect
|
||||
Clob clobField;
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getFirstLobField() {
|
||||
return firstLobField;
|
||||
}
|
||||
|
||||
public void setFirstLobField(String firstLobField) {
|
||||
this.firstLobField = firstLobField;
|
||||
}
|
||||
|
||||
public String getSecondLobField() {
|
||||
return secondLobField;
|
||||
}
|
||||
|
||||
public void setSecondLobField(String secondLobField) {
|
||||
this.secondLobField = secondLobField;
|
||||
}
|
||||
|
||||
public Clob getClobField() {
|
||||
return clobField;
|
||||
}
|
||||
|
||||
public void setClobField(Clob clobField) {
|
||||
this.clobField = clobField;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String buildRecursively(int size, char baseChar) {
|
||||
StringBuilder buff = new StringBuilder();
|
||||
for ( int i = 0; i < size; i++ ) {
|
||||
buff.append( baseChar );
|
||||
}
|
||||
return buff.toString();
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@ import jakarta.persistence.GeneratedValue;
|
|||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Lob;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.Tuple;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
|
|
Loading…
Reference in New Issue