Skip to content

Commit fc84bf7

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

File tree

4 files changed

+100
-1
lines changed

4 files changed

+100
-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
@@ -25,6 +25,7 @@
2525
import org.hibernate.dialect.aggregate.OracleAggregateSupport;
2626
import org.hibernate.dialect.function.CommonFunctionFactory;
2727
import org.hibernate.dialect.function.ModeStatsModeEmulation;
28+
import org.hibernate.dialect.function.OracleExtractFunction;
2829
import org.hibernate.dialect.function.OracleTruncFunction;
2930
import org.hibernate.dialect.identity.IdentityColumnSupport;
3031
import org.hibernate.dialect.identity.Oracle12cIdentityColumnSupport;
@@ -386,6 +387,11 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
386387
functionFactory.arrayTrim_oracle();
387388
functionFactory.arrayFill_oracle();
388389
functionFactory.arrayToString_oracle();
390+
391+
functionContributions.getFunctionRegistry().register(
392+
"extract",
393+
new OracleExtractFunction( this, typeConfiguration )
394+
);
389395
}
390396

391397
@Override

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
@@ -50,7 +50,7 @@
5050
*/
5151
public class ExtractFunction extends AbstractSqmFunctionDescriptor implements FunctionRenderer {
5252

53-
private final Dialect dialect;
53+
final Dialect dialect;
5454

5555
public ExtractFunction(Dialect dialect, TypeConfiguration typeConfiguration) {
5656
super(
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.dialect.function;
8+
9+
import jakarta.persistence.TemporalType;
10+
import org.hibernate.dialect.Dialect;
11+
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
12+
import org.hibernate.query.ReturnableType;
13+
import org.hibernate.query.sqm.TemporalUnit;
14+
import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
15+
import org.hibernate.sql.ast.SqlAstTranslator;
16+
import org.hibernate.sql.ast.spi.SqlAppender;
17+
import org.hibernate.sql.ast.tree.SqlAstNode;
18+
import org.hibernate.sql.ast.tree.expression.Expression;
19+
import org.hibernate.sql.ast.tree.expression.ExtractUnit;
20+
import org.hibernate.type.spi.TypeConfiguration;
21+
22+
import java.util.List;
23+
24+
import static org.hibernate.query.sqm.TemporalUnit.EPOCH;
25+
import static org.hibernate.type.spi.TypeConfiguration.getSqlTemporalType;
26+
27+
public class OracleExtractFunction extends ExtractFunction {
28+
public OracleExtractFunction(Dialect dialect, TypeConfiguration typeConfiguration) {
29+
super( dialect, typeConfiguration );
30+
}
31+
32+
@Override
33+
public void render(
34+
SqlAppender sqlAppender,
35+
List<? extends SqlAstNode> sqlAstArguments,
36+
ReturnableType<?> returnType,
37+
SqlAstTranslator<?> walker) {
38+
new PatternRenderer( extractPattern( sqlAstArguments ) ).render( sqlAppender, sqlAstArguments, walker );
39+
}
40+
41+
@SuppressWarnings("deprecation")
42+
private String extractPattern(List<? extends SqlAstNode> sqlAstArguments) {
43+
final ExtractUnit field = (ExtractUnit) sqlAstArguments.get( 0 );
44+
final TemporalUnit unit = field.getUnit();
45+
if ( unit == EPOCH ) {
46+
final Expression expression = (Expression) sqlAstArguments.get( 1 );
47+
final JdbcMappingContainer type = expression.getExpressionType();
48+
final TemporalType temporalType = type != null ? getSqlTemporalType( type ) : null;
49+
if ( temporalType == TemporalType.DATE ) {
50+
return "trunc((cast(from_tz(cast(?2 as timestamp),'UTC') as date) - date '1970-1-1')*86400)";
51+
}
52+
}
53+
return dialect.extractPattern( unit );
54+
}
55+
}

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
@@ -57,7 +57,9 @@
5757
import java.time.LocalDateTime;
5858
import java.time.LocalTime;
5959
import java.time.OffsetDateTime;
60+
import java.time.ZoneId;
6061
import java.time.ZoneOffset;
62+
import java.time.ZonedDateTime;
6163
import java.time.temporal.ChronoUnit;
6264
import java.util.Date;
6365
import java.util.List;
@@ -2572,4 +2574,40 @@ public void testCtidColumnFunction(SessionFactoryScope scope) {
25722574
.getSingleResultOrNull();
25732575
});
25742576
}
2577+
2578+
@Test
2579+
@JiraKey("HHH-18837")
2580+
public void testEpochFunction(SessionFactoryScope scope) {
2581+
2582+
LocalDate someLocalDate = LocalDate.of( 2013, 7, 5 );
2583+
LocalDateTime someLocalDateTime = someLocalDate.atStartOfDay();
2584+
Date someDate = Date.from( someLocalDateTime.toInstant( ZoneOffset.UTC ) );
2585+
ZonedDateTime someZonedDateTime = ZonedDateTime.of( someLocalDate, LocalTime.MIN,
2586+
ZoneId.of( "Europe/Vienna" ) );
2587+
2588+
scope.inTransaction( session -> {
2589+
EntityOfBasics entityOfBasics = new EntityOfBasics();
2590+
entityOfBasics.setId( 124 );
2591+
entityOfBasics.setTheDate( someDate );
2592+
entityOfBasics.setTheLocalDate( someLocalDate );
2593+
entityOfBasics.setTheLocalDateTime( someLocalDateTime );
2594+
entityOfBasics.setTheZonedDateTime( someZonedDateTime );
2595+
session.persist( entityOfBasics );
2596+
2597+
assertEquals( someDate.toInstant().toEpochMilli() / 1000,
2598+
session.createSelectionQuery( "select epoch(a.theDate) from EntityOfBasics a where id=124",
2599+
Long.class ).getSingleResult() );
2600+
assertEquals( someLocalDate.atStartOfDay( ZoneOffset.UTC ).toInstant().toEpochMilli() / 1000,
2601+
session.createSelectionQuery( "select epoch(a.theLocalDate) from EntityOfBasics a where id=124",
2602+
Long.class ).getSingleResult() );
2603+
assertEquals( someLocalDateTime.toEpochSecond( ZoneOffset.UTC ), session.createSelectionQuery(
2604+
"select epoch(a.theLocalDateTime) from EntityOfBasics a where id=124",
2605+
Long.class ).getSingleResult() );
2606+
assertEquals( someZonedDateTime.toEpochSecond(), session.createSelectionQuery(
2607+
"select epoch(a.theZonedDateTime) from EntityOfBasics a where id=124",
2608+
Long.class ).getSingleResult() );
2609+
2610+
session.remove( entityOfBasics );
2611+
} );
2612+
}
25752613
}

0 commit comments

Comments
 (0)