Skip to content

Commit daa4989

Browse files
peter1123581321mbellade
authored andcommitted
HHH-18837 Oracle epoch extraction doesn't work with dates
1 parent d1b208b commit daa4989

File tree

4 files changed

+98
-1
lines changed

4 files changed

+98
-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;
@@ -417,6 +418,11 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
417418
functionFactory.hex( "rawtohex(?1)" );
418419
functionFactory.sha( "standard_hash(?1, 'SHA256')" );
419420
functionFactory.md5( "standard_hash(?1, 'MD5')" );
421+
422+
functionContributions.getFunctionRegistry().register(
423+
"extract",
424+
new OracleExtractFunction( this, typeConfiguration )
425+
);
420426
}
421427

422428
/**

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: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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.mapping.JdbcMappingContainer;
10+
import org.hibernate.metamodel.model.domain.ReturnableType;
11+
import org.hibernate.query.common.TemporalUnit;
12+
import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
13+
import org.hibernate.sql.ast.SqlAstTranslator;
14+
import org.hibernate.sql.ast.spi.SqlAppender;
15+
import org.hibernate.sql.ast.tree.SqlAstNode;
16+
import org.hibernate.sql.ast.tree.expression.Expression;
17+
import org.hibernate.sql.ast.tree.expression.ExtractUnit;
18+
import org.hibernate.type.spi.TypeConfiguration;
19+
20+
import java.util.List;
21+
22+
import static org.hibernate.query.common.TemporalUnit.EPOCH;
23+
import static org.hibernate.type.spi.TypeConfiguration.getSqlTemporalType;
24+
25+
public class OracleExtractFunction extends ExtractFunction {
26+
public OracleExtractFunction(Dialect dialect, TypeConfiguration typeConfiguration) {
27+
super( dialect, typeConfiguration );
28+
}
29+
30+
@Override
31+
public void render(
32+
SqlAppender sqlAppender,
33+
List<? extends SqlAstNode> sqlAstArguments,
34+
ReturnableType<?> returnType,
35+
SqlAstTranslator<?> walker) {
36+
new PatternRenderer( extractPattern( sqlAstArguments ) ).render( sqlAppender, sqlAstArguments, walker );
37+
}
38+
39+
@SuppressWarnings("deprecation")
40+
private String extractPattern(List<? extends SqlAstNode> sqlAstArguments) {
41+
final ExtractUnit field = (ExtractUnit) sqlAstArguments.get( 0 );
42+
final TemporalUnit unit = field.getUnit();
43+
if ( unit == EPOCH ) {
44+
final Expression expression = (Expression) sqlAstArguments.get( 1 );
45+
final JdbcMappingContainer type = expression.getExpressionType();
46+
final TemporalType temporalType = type != null ? getSqlTemporalType( type ) : null;
47+
if ( temporalType == TemporalType.DATE ) {
48+
return "trunc((cast(from_tz(cast(?2 as timestamp),'UTC') as date) - date '1970-1-1')*86400)";
49+
}
50+
}
51+
return dialect.extractPattern( unit );
52+
}
53+
}

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)