Skip to content

Commit 170063c

Browse files
HHH-18837 Oracle epoch extraction doesn't work with dates
1 parent 3cfeb8f commit 170063c

File tree

4 files changed

+96
-1
lines changed

4 files changed

+96
-1
lines changed

hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.hibernate.dialect.aggregate.OracleAggregateSupport;
1717
import org.hibernate.dialect.function.CommonFunctionFactory;
1818
import org.hibernate.dialect.function.ModeStatsModeEmulation;
19+
import org.hibernate.dialect.function.OracleExtractFunction;
1920
import org.hibernate.dialect.function.OracleTruncFunction;
2021
import org.hibernate.dialect.identity.IdentityColumnSupport;
2122
import org.hibernate.dialect.identity.Oracle12cIdentityColumnSupport;
@@ -418,6 +419,11 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
418419
functionFactory.hex( "rawtohex(?1)" );
419420
functionFactory.sha( "standard_hash(?1, 'SHA256')" );
420421
functionFactory.md5( "standard_hash(?1, 'MD5')" );
422+
423+
functionContributions.getFunctionRegistry().register(
424+
"extract",
425+
new OracleExtractFunction( this, typeConfiguration )
426+
);
421427
}
422428

423429
/**

hibernate-core/src/main/java/org/hibernate/dialect/function/ExtractFunction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
*/
4949
public class ExtractFunction extends AbstractSqmFunctionDescriptor implements FunctionRenderer {
5050

51-
private final Dialect dialect;
51+
final Dialect dialect;
5252

5353
public ExtractFunction(Dialect dialect, TypeConfiguration typeConfiguration) {
5454
super(
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.dialect.function;
6+
7+
import jakarta.persistence.TemporalType;
8+
import org.hibernate.dialect.Dialect;
9+
import org.hibernate.metamodel.model.domain.ReturnableType;
10+
import org.hibernate.query.common.TemporalUnit;
11+
import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
12+
import org.hibernate.sql.ast.SqlAstTranslator;
13+
import org.hibernate.sql.ast.spi.SqlAppender;
14+
import org.hibernate.sql.ast.tree.SqlAstNode;
15+
import org.hibernate.sql.ast.tree.expression.Expression;
16+
import org.hibernate.sql.ast.tree.expression.ExtractUnit;
17+
import org.hibernate.type.spi.TypeConfiguration;
18+
19+
import java.util.List;
20+
21+
import static org.hibernate.type.spi.TypeConfiguration.getSqlTemporalType;
22+
23+
public class OracleExtractFunction extends ExtractFunction {
24+
public OracleExtractFunction(Dialect dialect, TypeConfiguration typeConfiguration) {
25+
super( dialect, typeConfiguration );
26+
}
27+
28+
@Override
29+
public void render(
30+
SqlAppender sqlAppender,
31+
List<? extends SqlAstNode> sqlAstArguments,
32+
ReturnableType<?> returnType,
33+
SqlAstTranslator<?> walker) {
34+
new PatternRenderer( extractPattern( sqlAstArguments ) ).render( sqlAppender, sqlAstArguments, walker );
35+
}
36+
37+
@SuppressWarnings("deprecation")
38+
private String extractPattern(List<? extends SqlAstNode> sqlAstArguments) {
39+
final ExtractUnit field = (ExtractUnit) sqlAstArguments.get( 0 );
40+
final TemporalUnit unit = field.getUnit();
41+
final Expression expression = (Expression) sqlAstArguments.get( 1 );
42+
final TemporalType temporalType;
43+
if ( unit.equals( TemporalUnit.EPOCH ) && (temporalType = getSqlTemporalType(
44+
expression.getExpressionType() )) != null ) {
45+
return temporalType == TemporalType.DATE
46+
? "trunc((cast(from_tz(cast(?2 as timestamp),'UTC') as date) - date '1970-1-1')*86400)"
47+
: "trunc((cast(?2 at time zone 'UTC' as date) - date '1970-1-1')*86400)";
48+
}
49+
return dialect.extractPattern( unit );
50+
}
51+
}

hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@
5858
import java.time.LocalDateTime;
5959
import java.time.LocalTime;
6060
import java.time.OffsetDateTime;
61+
import java.time.ZoneId;
6162
import java.time.ZoneOffset;
63+
import java.time.ZonedDateTime;
6264
import java.time.temporal.ChronoUnit;
6365
import java.util.Date;
6466
import java.util.List;
@@ -2644,4 +2646,40 @@ public void testHexFunction(SessionFactoryScope scope) {
26442646
.getSingleResult().toUpperCase( Locale.ROOT ) );
26452647
});
26462648
}
2649+
2650+
@Test
2651+
@JiraKey("HHH-18837")
2652+
public void testEpochFunction(SessionFactoryScope scope) {
2653+
2654+
LocalDate someLocalDate = LocalDate.of( 2013, 7, 5 );
2655+
LocalDateTime someLocalDateTime = someLocalDate.atStartOfDay();
2656+
Date someDate = Date.from( someLocalDateTime.toInstant( ZoneOffset.UTC ) );
2657+
ZonedDateTime someZonedDateTime = ZonedDateTime.of( someLocalDate, LocalTime.MIN,
2658+
ZoneId.of( "Europe/Vienna" ) );
2659+
2660+
scope.inTransaction( session -> {
2661+
EntityOfBasics entityOfBasics = new EntityOfBasics();
2662+
entityOfBasics.setId( 124 );
2663+
entityOfBasics.setTheDate( someDate );
2664+
entityOfBasics.setTheLocalDate( someLocalDate );
2665+
entityOfBasics.setTheLocalDateTime( someLocalDateTime );
2666+
entityOfBasics.setTheZonedDateTime( someZonedDateTime );
2667+
session.persist( entityOfBasics );
2668+
2669+
assertEquals( someDate.toInstant().toEpochMilli() / 1000,
2670+
session.createSelectionQuery( "select epoch(a.theDate) from EntityOfBasics a where id=124",
2671+
Long.class ).getSingleResult() );
2672+
assertEquals( someLocalDate.atStartOfDay( ZoneOffset.UTC ).toInstant().toEpochMilli() / 1000,
2673+
session.createSelectionQuery( "select epoch(a.theLocalDate) from EntityOfBasics a where id=124",
2674+
Long.class ).getSingleResult() );
2675+
assertEquals( someLocalDateTime.toEpochSecond( ZoneOffset.UTC ), session.createSelectionQuery(
2676+
"select epoch(a.theLocalDateTime) from EntityOfBasics a where id=124",
2677+
Long.class ).getSingleResult() );
2678+
assertEquals( someZonedDateTime.toEpochSecond(), session.createSelectionQuery(
2679+
"select epoch(a.theZonedDateTime) from EntityOfBasics a where id=124",
2680+
Long.class ).getSingleResult() );
2681+
2682+
session.remove( entityOfBasics );
2683+
} );
2684+
}
26472685
}

0 commit comments

Comments
 (0)