HHH-16130 New dateTrunc criteria function
This commit is contained in:
parent
e170eb33d1
commit
a100dfceda
|
@ -38,7 +38,6 @@ import org.hibernate.persister.entity.Lockable;
|
||||||
import org.hibernate.query.SemanticException;
|
import org.hibernate.query.SemanticException;
|
||||||
import org.hibernate.query.sqm.IntervalType;
|
import org.hibernate.query.sqm.IntervalType;
|
||||||
import org.hibernate.query.sqm.TemporalUnit;
|
import org.hibernate.query.sqm.TemporalUnit;
|
||||||
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
|
|
||||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
@ -53,6 +52,7 @@ import org.hibernate.type.StandardBasicTypes;
|
||||||
import jakarta.persistence.TemporalType;
|
import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
import static org.hibernate.dialect.SimpleDatabaseVersion.ZERO_VERSION;
|
import static org.hibernate.dialect.SimpleDatabaseVersion.ZERO_VERSION;
|
||||||
|
import static org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers.useArgType;
|
||||||
import static org.hibernate.type.SqlTypes.BIGINT;
|
import static org.hibernate.type.SqlTypes.BIGINT;
|
||||||
import static org.hibernate.type.SqlTypes.BINARY;
|
import static org.hibernate.type.SqlTypes.BINARY;
|
||||||
import static org.hibernate.type.SqlTypes.BLOB;
|
import static org.hibernate.type.SqlTypes.BLOB;
|
||||||
|
@ -394,7 +394,7 @@ public class SpannerDialect extends Dialect {
|
||||||
.setExactArgumentCount( 3 )
|
.setExactArgumentCount( 3 )
|
||||||
.register();
|
.register();
|
||||||
functionContributions.getFunctionRegistry().namedDescriptorBuilder( "date_trunc" )
|
functionContributions.getFunctionRegistry().namedDescriptorBuilder( "date_trunc" )
|
||||||
.setReturnTypeResolver( StandardFunctionReturnTypeResolvers.useArgType( 2 ) )
|
.setReturnTypeResolver( useArgType( 1 ) )
|
||||||
.setExactArgumentCount( 2 )
|
.setExactArgumentCount( 2 )
|
||||||
.register();
|
.register();
|
||||||
functionContributions.getFunctionRegistry().namedDescriptorBuilder( "date_from_unix_date" )
|
functionContributions.getFunctionRegistry().namedDescriptorBuilder( "date_from_unix_date" )
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom {@link TruncFunction} for Sybase which uses a dialect-specific emulation function for datetimes
|
* Custom {@link TruncFunction} for Sybase which uses a dialect-specific emulation function for datetimes
|
||||||
|
@ -94,10 +95,16 @@ public class SybaseTruncFunction extends TruncFunction {
|
||||||
sqlAppender.append( '(' );
|
sqlAppender.append( '(' );
|
||||||
sqlAppender.append( "datetime,substring(convert(varchar," );
|
sqlAppender.append( "datetime,substring(convert(varchar," );
|
||||||
sqlAstArguments.get( 0 ).accept( walker );
|
sqlAstArguments.get( 0 ).accept( walker );
|
||||||
sqlAppender.append( ",21),1,17-len(" );
|
sqlAppender.append( ",21),1,17" );
|
||||||
sqlAstArguments.get( 1 ).accept( walker );
|
if ( sqlAstArguments.size() > 1 ) {
|
||||||
sqlAppender.append( "))+" );
|
sqlAppender.append( "-len(" );
|
||||||
sqlAstArguments.get( 1 ).accept( walker );
|
sqlAstArguments.get( 1 ).accept( walker );
|
||||||
|
sqlAppender.append( "))+" );
|
||||||
|
sqlAstArguments.get( 1 ).accept( walker );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ')' );
|
||||||
|
}
|
||||||
sqlAppender.append( ",21)" );
|
sqlAppender.append( ",21)" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,22 +134,25 @@ public class SybaseTruncFunction extends TruncFunction {
|
||||||
literal = ":00";
|
literal = ":00";
|
||||||
break;
|
break;
|
||||||
case SECOND:
|
case SECOND:
|
||||||
literal = "";
|
literal = null;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new UnsupportedOperationException( "Temporal unit not supported [" + temporalUnit + "]" );
|
throw new UnsupportedOperationException( "Temporal unit not supported [" + temporalUnit + "]" );
|
||||||
}
|
}
|
||||||
final SqmTypedNode<?> datetime = arguments.get( 0 );
|
|
||||||
final SqmLiteral<String> sqmLiteral = new SqmLiteral<>(
|
|
||||||
literal,
|
|
||||||
typeConfiguration.getBasicTypeForJavaType( String.class ),
|
|
||||||
nodeBuilder
|
|
||||||
);
|
|
||||||
|
|
||||||
return new SelfRenderingSqmFunction<>(
|
return new SelfRenderingSqmFunction<>(
|
||||||
this,
|
this,
|
||||||
this,
|
this,
|
||||||
asList( datetime, sqmLiteral ),
|
literal == null ?
|
||||||
|
singletonList( arguments.get( 0 ) ) :
|
||||||
|
asList(
|
||||||
|
arguments.get( 0 ),
|
||||||
|
new SqmLiteral<>(
|
||||||
|
literal,
|
||||||
|
typeConfiguration.getBasicTypeForJavaType( String.class ),
|
||||||
|
nodeBuilder
|
||||||
|
)
|
||||||
|
),
|
||||||
impliedResultType,
|
impliedResultType,
|
||||||
null,
|
null,
|
||||||
getReturnTypeResolver(),
|
getReturnTypeResolver(),
|
||||||
|
|
|
@ -36,6 +36,7 @@ import org.hibernate.Incubating;
|
||||||
import org.hibernate.query.sqm.FrameKind;
|
import org.hibernate.query.sqm.FrameKind;
|
||||||
import org.hibernate.query.sqm.NullPrecedence;
|
import org.hibernate.query.sqm.NullPrecedence;
|
||||||
import org.hibernate.query.sqm.SortOrder;
|
import org.hibernate.query.sqm.SortOrder;
|
||||||
|
import org.hibernate.query.sqm.TemporalUnit;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -997,6 +998,9 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
|
||||||
@Incubating
|
@Incubating
|
||||||
JpaFunction<Float> second(Expression<? extends TemporalAccessor> datetime);
|
JpaFunction<Float> second(Expression<? extends TemporalAccessor> datetime);
|
||||||
|
|
||||||
|
@Incubating
|
||||||
|
<T extends TemporalAccessor> JpaFunction<T> truncate(Expression<T> datetime, TemporalUnit temporalUnit);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see #overlay(Expression, Expression, Expression, Expression)
|
* @see #overlay(Expression, Expression, Expression, Expression)
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -52,6 +52,7 @@ import org.hibernate.query.criteria.JpaWindow;
|
||||||
import org.hibernate.query.criteria.JpaWindowFrame;
|
import org.hibernate.query.criteria.JpaWindowFrame;
|
||||||
import org.hibernate.query.sqm.NullPrecedence;
|
import org.hibernate.query.sqm.NullPrecedence;
|
||||||
import org.hibernate.query.sqm.SortOrder;
|
import org.hibernate.query.sqm.SortOrder;
|
||||||
|
import org.hibernate.query.sqm.TemporalUnit;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||||
|
|
||||||
import jakarta.persistence.Tuple;
|
import jakarta.persistence.Tuple;
|
||||||
|
@ -1303,6 +1304,11 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
|
||||||
return criteriaBuilder.second( datetime );
|
return criteriaBuilder.second( datetime );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends TemporalAccessor> JpaFunction<T> truncate(Expression<T> datetime, TemporalUnit temporalUnit) {
|
||||||
|
return criteriaBuilder.truncate( datetime, temporalUnit );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JpaFunction<String> overlay(Expression<String> string, String replacement, int start) {
|
public JpaFunction<String> overlay(Expression<String> string, String replacement, int start) {
|
||||||
return criteriaBuilder.overlay( string, replacement, start );
|
return criteriaBuilder.overlay( string, replacement, start );
|
||||||
|
|
|
@ -2650,6 +2650,19 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
|
||||||
return extract( datetime, TemporalUnit.SECOND, Float.class );
|
return extract( datetime, TemporalUnit.SECOND, Float.class );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends TemporalAccessor> SqmFunction<T> truncate(Expression<T> datetime, TemporalUnit temporalUnit) {
|
||||||
|
return getFunctionDescriptor( "trunc" ).generateSqmExpression(
|
||||||
|
asList(
|
||||||
|
(SqmTypedNode<?>) datetime,
|
||||||
|
new SqmExtractUnit<>( temporalUnit, getIntegerType(), this )
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
queryEngine,
|
||||||
|
getJpaMetamodel().getTypeConfiguration()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmFunction<String> overlay(Expression<String> string, String replacement, int start) {
|
public SqmFunction<String> overlay(Expression<String> string, String replacement, int start) {
|
||||||
return overlay( string, replacement, value( start ), null );
|
return overlay( string, replacement, value( start ), null );
|
||||||
|
|
|
@ -12,12 +12,15 @@ import java.text.SimpleDateFormat;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.dialect.CockroachDialect;
|
import org.hibernate.dialect.CockroachDialect;
|
||||||
|
import org.hibernate.dialect.DerbyDialect;
|
||||||
import org.hibernate.dialect.PostgreSQLDialect;
|
import org.hibernate.dialect.PostgreSQLDialect;
|
||||||
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
|
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
|
||||||
|
import org.hibernate.query.sqm.TemporalUnit;
|
||||||
|
|
||||||
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
||||||
import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
|
import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
|
||||||
|
@ -438,7 +441,7 @@ public class CriteriaBuilderNonStandardFunctionsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@JiraKey("HHH-16185")
|
@JiraKey("HHH-16185")
|
||||||
public void testCustomFunctionTrunc(SessionFactoryScope scope) {
|
public void testNumericTruncFunction(SessionFactoryScope scope) {
|
||||||
scope.inTransaction( session -> {
|
scope.inTransaction( session -> {
|
||||||
final CriteriaBuilder cb = session.getCriteriaBuilder();
|
final CriteriaBuilder cb = session.getCriteriaBuilder();
|
||||||
final CriteriaQuery<Tuple> query = cb.createTupleQuery();
|
final CriteriaQuery<Tuple> query = cb.createTupleQuery();
|
||||||
|
@ -463,4 +466,41 @@ public class CriteriaBuilderNonStandardFunctionsTest {
|
||||||
assertEquals( 32.923d, result.get( 7 ) );
|
assertEquals( 32.923d, result.get( 7 ) );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@JiraKey("HHH-16130")
|
||||||
|
@SkipForDialect(dialectClass = DerbyDialect.class, reason = "Derby doesn't support any form of date truncation")
|
||||||
|
public void testDateTruncFunction(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
|
||||||
|
CriteriaQuery<Tuple> query = cb.createTupleQuery();
|
||||||
|
Root<EntityOfBasics> from = query.from( EntityOfBasics.class );
|
||||||
|
|
||||||
|
Expression<LocalDateTime> theLocalDateTime = from.get( "theLocalDateTime" );
|
||||||
|
query.multiselect(
|
||||||
|
from.get( "id" ),
|
||||||
|
cb.truncate( theLocalDateTime, TemporalUnit.YEAR ),
|
||||||
|
cb.truncate( theLocalDateTime, TemporalUnit.MONTH ),
|
||||||
|
cb.truncate( theLocalDateTime, TemporalUnit.DAY ),
|
||||||
|
cb.truncate( theLocalDateTime, TemporalUnit.HOUR ),
|
||||||
|
cb.truncate( theLocalDateTime, TemporalUnit.MINUTE ),
|
||||||
|
cb.truncate( theLocalDateTime, TemporalUnit.SECOND )
|
||||||
|
).where( cb.isNotNull( theLocalDateTime ) );
|
||||||
|
|
||||||
|
Tuple result = session.createQuery( query ).getSingleResult();
|
||||||
|
EntityOfBasics eob = session.find( EntityOfBasics.class, result.get( 0 ) );
|
||||||
|
assertEquals(
|
||||||
|
eob.getTheLocalDateTime().withMonth( 1 ).withDayOfMonth( 1 ).truncatedTo( ChronoUnit.DAYS ),
|
||||||
|
result.get( 1 )
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
eob.getTheLocalDateTime().withDayOfMonth( 1 ).truncatedTo( ChronoUnit.DAYS ),
|
||||||
|
result.get( 2 )
|
||||||
|
);
|
||||||
|
assertEquals( eob.getTheLocalDateTime().truncatedTo( ChronoUnit.DAYS ), result.get( 3 ) );
|
||||||
|
assertEquals( eob.getTheLocalDateTime().truncatedTo( ChronoUnit.HOURS ), result.get( 4 ) );
|
||||||
|
assertEquals( eob.getTheLocalDateTime().truncatedTo( ChronoUnit.MINUTES ), result.get( 5 ) );
|
||||||
|
assertEquals( eob.getTheLocalDateTime().truncatedTo( ChronoUnit.SECONDS ), result.get( 6 ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue