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.sqm.IntervalType;
|
||||
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.SqlAstTranslatorFactory;
|
||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||
|
@ -53,6 +52,7 @@ import org.hibernate.type.StandardBasicTypes;
|
|||
import jakarta.persistence.TemporalType;
|
||||
|
||||
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.BINARY;
|
||||
import static org.hibernate.type.SqlTypes.BLOB;
|
||||
|
@ -394,7 +394,7 @@ public class SpannerDialect extends Dialect {
|
|||
.setExactArgumentCount( 3 )
|
||||
.register();
|
||||
functionContributions.getFunctionRegistry().namedDescriptorBuilder( "date_trunc" )
|
||||
.setReturnTypeResolver( StandardFunctionReturnTypeResolvers.useArgType( 2 ) )
|
||||
.setReturnTypeResolver( useArgType( 1 ) )
|
||||
.setExactArgumentCount( 2 )
|
||||
.register();
|
||||
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 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
|
||||
|
@ -94,10 +95,16 @@ public class SybaseTruncFunction extends TruncFunction {
|
|||
sqlAppender.append( '(' );
|
||||
sqlAppender.append( "datetime,substring(convert(varchar," );
|
||||
sqlAstArguments.get( 0 ).accept( walker );
|
||||
sqlAppender.append( ",21),1,17-len(" );
|
||||
sqlAstArguments.get( 1 ).accept( walker );
|
||||
sqlAppender.append( "))+" );
|
||||
sqlAstArguments.get( 1 ).accept( walker );
|
||||
sqlAppender.append( ",21),1,17" );
|
||||
if ( sqlAstArguments.size() > 1 ) {
|
||||
sqlAppender.append( "-len(" );
|
||||
sqlAstArguments.get( 1 ).accept( walker );
|
||||
sqlAppender.append( "))+" );
|
||||
sqlAstArguments.get( 1 ).accept( walker );
|
||||
}
|
||||
else {
|
||||
sqlAppender.append( ')' );
|
||||
}
|
||||
sqlAppender.append( ",21)" );
|
||||
}
|
||||
|
||||
|
@ -127,22 +134,25 @@ public class SybaseTruncFunction extends TruncFunction {
|
|||
literal = ":00";
|
||||
break;
|
||||
case SECOND:
|
||||
literal = "";
|
||||
literal = null;
|
||||
break;
|
||||
default:
|
||||
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<>(
|
||||
this,
|
||||
this,
|
||||
asList( datetime, sqmLiteral ),
|
||||
literal == null ?
|
||||
singletonList( arguments.get( 0 ) ) :
|
||||
asList(
|
||||
arguments.get( 0 ),
|
||||
new SqmLiteral<>(
|
||||
literal,
|
||||
typeConfiguration.getBasicTypeForJavaType( String.class ),
|
||||
nodeBuilder
|
||||
)
|
||||
),
|
||||
impliedResultType,
|
||||
null,
|
||||
getReturnTypeResolver(),
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.hibernate.Incubating;
|
|||
import org.hibernate.query.sqm.FrameKind;
|
||||
import org.hibernate.query.sqm.NullPrecedence;
|
||||
import org.hibernate.query.sqm.SortOrder;
|
||||
import org.hibernate.query.sqm.TemporalUnit;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||
|
||||
/**
|
||||
|
@ -997,6 +998,9 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
|
|||
@Incubating
|
||||
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)
|
||||
*/
|
||||
|
|
|
@ -52,6 +52,7 @@ import org.hibernate.query.criteria.JpaWindow;
|
|||
import org.hibernate.query.criteria.JpaWindowFrame;
|
||||
import org.hibernate.query.sqm.NullPrecedence;
|
||||
import org.hibernate.query.sqm.SortOrder;
|
||||
import org.hibernate.query.sqm.TemporalUnit;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||
|
||||
import jakarta.persistence.Tuple;
|
||||
|
@ -1303,6 +1304,11 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
|
|||
return criteriaBuilder.second( datetime );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends TemporalAccessor> JpaFunction<T> truncate(Expression<T> datetime, TemporalUnit temporalUnit) {
|
||||
return criteriaBuilder.truncate( datetime, temporalUnit );
|
||||
}
|
||||
|
||||
@Override
|
||||
public JpaFunction<String> overlay(Expression<String> string, String replacement, int start) {
|
||||
return criteriaBuilder.overlay( string, replacement, start );
|
||||
|
|
|
@ -2650,6 +2650,19 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
|
|||
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
|
||||
public SqmFunction<String> overlay(Expression<String> string, String replacement, int start) {
|
||||
return overlay( string, replacement, value( start ), null );
|
||||
|
|
|
@ -12,12 +12,15 @@ import java.text.SimpleDateFormat;
|
|||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.dialect.CockroachDialect;
|
||||
import org.hibernate.dialect.DerbyDialect;
|
||||
import org.hibernate.dialect.PostgreSQLDialect;
|
||||
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.gambit.EntityOfBasics;
|
||||
|
@ -438,7 +441,7 @@ public class CriteriaBuilderNonStandardFunctionsTest {
|
|||
|
||||
@Test
|
||||
@JiraKey("HHH-16185")
|
||||
public void testCustomFunctionTrunc(SessionFactoryScope scope) {
|
||||
public void testNumericTruncFunction(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
final CriteriaBuilder cb = session.getCriteriaBuilder();
|
||||
final CriteriaQuery<Tuple> query = cb.createTupleQuery();
|
||||
|
@ -463,4 +466,41 @@ public class CriteriaBuilderNonStandardFunctionsTest {
|
|||
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