HHH-13785 : HQL/Criteria function support
- EXTRACT function - LOCAL_DATETIME function - LOCAL_DATE function - LOCAL_TIME function
This commit is contained in:
parent
77377337d6
commit
6e0d15b134
|
@ -0,0 +1,2 @@
|
|||
## Ignore IntelliJ Antlr Plugin's output
|
||||
gen/
|
|
@ -138,6 +138,7 @@ CURRENT_INSTANT : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [iI] [nN] [sS] [tT] [a
|
|||
CURRENT_TIME : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [tT] [iI] [mM] [eE];
|
||||
CURRENT_TIMESTAMP : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [tT] [iI] [mM] [eE] [sS] [tT] [aA] [mM] [pP];
|
||||
DAY : [dD] [aA] [yY];
|
||||
DATE : [dD] [aA] [tT] [eE];
|
||||
DELETE : [dD] [eE] [lL] [eE] [tT] [eE];
|
||||
DESC : [dD] [eE] [sS] [cC];
|
||||
DISTINCT : [dD] [iI] [sS] [tT] [iI] [nN] [cC] [tT];
|
||||
|
@ -178,6 +179,9 @@ LIMIT : [lL] [iI] [mM] [iI] [tT];
|
|||
LIST : [lL] [iI] [sS] [tT];
|
||||
LN : [lL] [nN];
|
||||
LOCATE : [lL] [oO] [cC] [aA] [tT] [eE];
|
||||
LOCAL_DATE : [lL] [oO] [cC] [aA] [lL] '_' [dD] [aA] [tT] [eE];
|
||||
LOCAL_DATETIME : [lL] [oO] [cC] [aA] [lL] '_' [dD] [aA] [tT] [eE] [tT] [iI] [mM] [eE];
|
||||
LOCAL_TIME : [lL] [oO] [cC] [aA] [lL] '_' [tT] [iI] [mM] [eE];
|
||||
LOWER : [lL] [oO] [wW] [eE] [rR];
|
||||
MAP : [mM] [aA] [pP];
|
||||
MAX : [mM] [aA] [xX];
|
||||
|
@ -193,6 +197,7 @@ MINUTE : [mM] [iI] [nN] [uU] [tT] [eE];
|
|||
MOD : [mM] [oO] [dD];
|
||||
MONTH : [mM] [oO] [nN] [tT] [hH];
|
||||
NEW : [nN] [eE] [wW];
|
||||
NANOSECOND : [nN] [aA] [nN] [oO] [sS] [eE] [cC] [oO] [nN] [dD];
|
||||
NOT : [nN] [oO] [tT];
|
||||
NULLIF : [nN] [uU] [lL] [lL] [iI] [fF];
|
||||
OBJECT : [oO] [bB] [jJ] [eE] [cC] [tT];
|
||||
|
@ -218,6 +223,7 @@ STR : [sS] [tT] [rR];
|
|||
SUBSTRING : [sS] [uU] [bB] [sS] [tT] [rR] [iI] [nN] [gG];
|
||||
SUM : [sS] [uU] [mM];
|
||||
THEN : [tT] [hH] [eE] [nN];
|
||||
TIME : [tT] [iI] [mM] [eE];
|
||||
TIMEZONE_HOUR : [tT] [iI] [mM] [eE] [zZ] [oO] [nN] [eE] '_' [hH] [oO] [uU] [rR];
|
||||
TIMEZONE_MINUTE : [tT] [iI] [mM] [eE] [zZ] [oO] [nN] [eE] '_' [mM] [iI] [nN] [uU] [tT] [eE];
|
||||
TRAILING : [tT] [rR] [aA] [iI] [lL] [iI] [nN] [gG];
|
||||
|
|
|
@ -617,6 +617,9 @@ standardFunction
|
|||
| currentTimeFunction
|
||||
| currentTimestampFunction
|
||||
| currentInstantFunction
|
||||
| localDateFunction
|
||||
| localDateTimeFunction
|
||||
| localTimeFunction
|
||||
;
|
||||
|
||||
|
||||
|
@ -808,6 +811,18 @@ currentInstantFunction
|
|||
: CURRENT_INSTANT (LEFT_PAREN RIGHT_PAREN)?
|
||||
;
|
||||
|
||||
localDateTimeFunction
|
||||
: LOCAL_DATETIME (LEFT_PAREN RIGHT_PAREN)?
|
||||
;
|
||||
|
||||
localDateFunction
|
||||
: LOCAL_DATE (LEFT_PAREN RIGHT_PAREN)?
|
||||
;
|
||||
|
||||
localTimeFunction
|
||||
: LOCAL_TIME (LEFT_PAREN RIGHT_PAREN)?
|
||||
;
|
||||
|
||||
formatFunction
|
||||
: FORMAT LEFT_PAREN expression AS format RIGHT_PAREN
|
||||
;
|
||||
|
@ -906,10 +921,12 @@ identifier
|
|||
| COUNT
|
||||
| CROSS
|
||||
| CURRENT_DATE
|
||||
| CURRENT_DATETIME
|
||||
| CURRENT_INSTANT
|
||||
| CURRENT_TIME
|
||||
| CURRENT_TIMESTAMP
|
||||
| DAY
|
||||
| DATE
|
||||
| DELETE
|
||||
| DESC
|
||||
| DISTINCT
|
||||
|
@ -963,6 +980,7 @@ identifier
|
|||
| MINUTE
|
||||
| MOD
|
||||
| MONTH
|
||||
| NANOSECOND
|
||||
| NEW
|
||||
| NOT
|
||||
| NULLIF
|
||||
|
@ -989,6 +1007,7 @@ identifier
|
|||
| SUBSTRING
|
||||
| SUM
|
||||
| THEN
|
||||
| TIME
|
||||
| TIMEZONE_HOUR
|
||||
| TIMEZONE_MINUTE
|
||||
| TRAILING
|
||||
|
|
|
@ -711,6 +711,7 @@ public class CommonFunctionFactory {
|
|||
}
|
||||
|
||||
public static void currentDateTimeTimestamp(QueryEngine queryEngine) {
|
||||
// Legacy JDK `Date`-based functions
|
||||
queryEngine.getSqmFunctionRegistry().noArgsBuilder( "current_time" )
|
||||
.setInvariantType( StandardBasicTypes.TIME )
|
||||
.register();
|
||||
|
@ -721,15 +722,18 @@ public class CommonFunctionFactory {
|
|||
.setInvariantType( StandardBasicTypes.TIMESTAMP )
|
||||
.register();
|
||||
|
||||
// "JDK 8" temporal-type functions.
|
||||
// - These are essentially aliases for the `current_XYZ` forms
|
||||
// but defining JDK 8 temporal type return values
|
||||
queryEngine.getSqmFunctionRegistry().noArgsBuilder( "current_time" )
|
||||
.setInvariantType( StandardBasicTypes.LOCAL_TIME )
|
||||
.register();
|
||||
.register( "local_time" );
|
||||
queryEngine.getSqmFunctionRegistry().noArgsBuilder( "current_date" )
|
||||
.setInvariantType( StandardBasicTypes.LOCAL_DATE )
|
||||
.register();
|
||||
.register( "local_date" );
|
||||
queryEngine.getSqmFunctionRegistry().noArgsBuilder( "current_timestamp" )
|
||||
.setInvariantType( StandardBasicTypes.LOCAL_DATE_TIME )
|
||||
.register();
|
||||
.register( "local_datetime");
|
||||
queryEngine.getSqmFunctionRegistry().noArgsBuilder( "current_timestamp" )
|
||||
.setInvariantType( StandardBasicTypes.INSTANT )
|
||||
.register( "current_instant" );
|
||||
|
|
|
@ -1507,6 +1507,39 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitLocalDateTimeFunction(HqlParser.LocalDateTimeFunctionContext ctx) {
|
||||
//noinspection unchecked
|
||||
return new SqmFunction(
|
||||
"local_datetime",
|
||||
getFunctionDescriptor( "local_datetime" ),
|
||||
StandardBasicTypes.TIMESTAMP,
|
||||
creationContext.getNodeBuilder()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitLocalDateFunction(HqlParser.LocalDateFunctionContext ctx) {
|
||||
//noinspection unchecked
|
||||
return new SqmFunction(
|
||||
"local_date",
|
||||
getFunctionDescriptor( "local_date" ),
|
||||
StandardBasicTypes.TIMESTAMP,
|
||||
creationContext.getNodeBuilder()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitLocalTimeFunction(HqlParser.LocalTimeFunctionContext ctx) {
|
||||
//noinspection unchecked
|
||||
return new SqmFunction(
|
||||
"local_time",
|
||||
getFunctionDescriptor( "local_time" ),
|
||||
StandardBasicTypes.TIMESTAMP,
|
||||
creationContext.getNodeBuilder()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmExpression visitCurrentInstantFunction(HqlParser.CurrentInstantFunctionContext ctx) {
|
||||
//noinspection unchecked
|
||||
|
@ -1901,11 +1934,15 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
|
|||
|
||||
@Override
|
||||
public List<SqmTypedNode<?>> visitNonStandardFunctionArguments(HqlParser.NonStandardFunctionArgumentsContext ctx) {
|
||||
if ( ctx == null || ctx.expression() == null ) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final List<SqmTypedNode<?>> arguments = new ArrayList<>();
|
||||
|
||||
for ( int i=0, size=ctx.expression().size(); i<size; i++ ) {
|
||||
for ( int i = 0, size = ctx.expression().size(); i < size; i++ ) {
|
||||
// we handle the final argument differently...
|
||||
if ( i == size-1 ) {
|
||||
if ( i == size - 1 ) {
|
||||
arguments.add( visitFinalFunctionArgument( ctx.expression( i ) ) );
|
||||
}
|
||||
else {
|
||||
|
@ -2109,56 +2146,56 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
|
|||
@Override
|
||||
public Object visitDatetimeField(HqlParser.DatetimeFieldContext ctx) {
|
||||
NodeBuilder nodeBuilder = creationContext.getNodeBuilder();
|
||||
if (ctx.DAY()!=null) {
|
||||
if ( ctx.DAY() != null ) {
|
||||
return new SqmExtractUnit<>(
|
||||
TemporalUnit.DAY,
|
||||
resolveExpressableTypeBasic( Integer.class ),
|
||||
nodeBuilder
|
||||
);
|
||||
}
|
||||
if (ctx.MONTH()!=null) {
|
||||
if ( ctx.MONTH() != null ) {
|
||||
return new SqmExtractUnit<>(
|
||||
TemporalUnit.MONTH,
|
||||
resolveExpressableTypeBasic( Integer.class ),
|
||||
nodeBuilder
|
||||
);
|
||||
}
|
||||
if (ctx.YEAR()!=null) {
|
||||
if ( ctx.YEAR() != null ) {
|
||||
return new SqmExtractUnit<>(
|
||||
TemporalUnit.YEAR,
|
||||
resolveExpressableTypeBasic( Integer.class ),
|
||||
nodeBuilder
|
||||
);
|
||||
}
|
||||
if (ctx.HOUR()!=null) {
|
||||
if ( ctx.HOUR() != null ) {
|
||||
return new SqmExtractUnit<>(
|
||||
TemporalUnit.HOUR,
|
||||
resolveExpressableTypeBasic( Integer.class ),
|
||||
nodeBuilder
|
||||
);
|
||||
}
|
||||
if (ctx.MINUTE()!=null) {
|
||||
if ( ctx.MINUTE() != null ) {
|
||||
return new SqmExtractUnit<>(
|
||||
TemporalUnit.MINUTE,
|
||||
resolveExpressableTypeBasic( Integer.class ),
|
||||
nodeBuilder
|
||||
);
|
||||
}
|
||||
if (ctx.SECOND()!=null) {
|
||||
if ( ctx.SECOND() != null ) {
|
||||
return new SqmExtractUnit<>(
|
||||
TemporalUnit.SECOND,
|
||||
resolveExpressableTypeBasic( Float.class ),
|
||||
nodeBuilder
|
||||
);
|
||||
}
|
||||
if (ctx.WEEK()!=null) {
|
||||
if ( ctx.WEEK() != null ) {
|
||||
return new SqmExtractUnit<>(
|
||||
TemporalUnit.WEEK,
|
||||
resolveExpressableTypeBasic( Integer.class ),
|
||||
nodeBuilder
|
||||
);
|
||||
}
|
||||
if (ctx.QUARTER()!=null) {
|
||||
if ( ctx.QUARTER() != null ) {
|
||||
return new SqmExtractUnit<>(
|
||||
TemporalUnit.QUARTER,
|
||||
resolveExpressableTypeBasic( Integer.class ),
|
||||
|
@ -2166,7 +2203,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
|
|||
);
|
||||
}
|
||||
|
||||
return super.visitDatetimeField(ctx);
|
||||
return super.visitDatetimeField( ctx );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -11,7 +11,6 @@ import java.sql.Time;
|
|||
import java.sql.Timestamp;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
||||
import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
|
||||
|
@ -82,6 +81,54 @@ public class StandardFunctionTests {
|
|||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void localDateTimeTests(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "select local_datetime from EntityOfBasics" ).list();
|
||||
session.createQuery( "select local_datetime() from EntityOfBasics" ).list();
|
||||
|
||||
session.createQuery( "select e from EntityOfBasics e where e.theTimestamp = local_datetime" ).list();
|
||||
session.createQuery( "select e from EntityOfBasics e where e.theTimestamp = local_datetime()()" ).list();
|
||||
|
||||
session.createQuery( "select e from EntityOfBasics e where local_datetime() between e.theTimestamp and e.theTimestamp" ).list();
|
||||
session.createQuery( "select e from EntityOfBasics e where local_datetime()() between e.theTimestamp and e.theTimestamp" ).list();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void localDateTests(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "select local_date from EntityOfBasics" ).list();
|
||||
session.createQuery( "select local_date() from EntityOfBasics" ).list();
|
||||
|
||||
session.createQuery( "select e from EntityOfBasics e where e.theTimestamp = local_date" ).list();
|
||||
session.createQuery( "select e from EntityOfBasics e where e.theTimestamp = local_date()()" ).list();
|
||||
|
||||
session.createQuery( "select e from EntityOfBasics e where local_date() between e.theTimestamp and e.theTimestamp" ).list();
|
||||
session.createQuery( "select e from EntityOfBasics e where local_date()() between e.theTimestamp and e.theTimestamp" ).list();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void localTimeTests(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "select local_time from EntityOfBasics" ).list();
|
||||
session.createQuery( "select local_time() from EntityOfBasics" ).list();
|
||||
|
||||
session.createQuery( "select e from EntityOfBasics e where e.theTimestamp = local_time" ).list();
|
||||
session.createQuery( "select e from EntityOfBasics e where e.theTimestamp = local_time()()" ).list();
|
||||
|
||||
session.createQuery( "select e from EntityOfBasics e where local_time() between e.theTimestamp and e.theTimestamp" ).list();
|
||||
session.createQuery( "select e from EntityOfBasics e where local_time()() between e.theTimestamp and e.theTimestamp" ).list();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcatFunction(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
|
@ -489,7 +536,6 @@ public class StandardFunctionTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
@FailureExpected
|
||||
public void testExtractFunction(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
|
@ -534,20 +580,44 @@ public class StandardFunctionTests {
|
|||
|
||||
session.createQuery("select extract(offset hour from e.theTime) from EntityOfBasics e")
|
||||
.list();
|
||||
session.createQuery("select extract(offset hour minute from e.theTime) from EntityOfBasics e")
|
||||
.list();
|
||||
|
||||
// the grammar rule is defined as `HOUR | MINUTE` so no idea how both ever worked.
|
||||
// session.createQuery("select extract(offset hour minute from e.theTime) from EntityOfBasics e")
|
||||
// .list();
|
||||
|
||||
session.createQuery("select extract(offset from e.theTimestamp) from EntityOfBasics e")
|
||||
.list();
|
||||
|
||||
session.createQuery("select extract(time from e.theTimestamp), extract(date from e.theTimestamp) from EntityOfBasics e")
|
||||
.list();
|
||||
session.createQuery("select extract(time from current datetime), extract(date from current datetime) from EntityOfBasics e")
|
||||
session.createQuery("select extract(time from local_datetime), extract(date from local_datetime) from EntityOfBasics e")
|
||||
.list();
|
||||
|
||||
session.createQuery("select extract(week of month from current date) from EntityOfBasics e")
|
||||
// Not a fan of these "current date", "current time" and "current timestamp" forms. They were meant to represent `LocalDate`,
|
||||
// `LocalTime` and `LocalDateTime` values. Which imo is just super confusing with `current_date`, `current_time` and
|
||||
// `current_timestamp` returning the `Date`, `Time` and `Timestamp` forms
|
||||
// session.createQuery("select extract(time from current datetime), extract(date from current datetime) from EntityOfBasics e")
|
||||
// .list();
|
||||
// So I added `local_date`, `local_time` and `local_datetime` functions instead. See the `localDateTests`, etc
|
||||
|
||||
session.createQuery("select extract(week of month from current_date) from EntityOfBasics e")
|
||||
.list();
|
||||
session.createQuery("select extract(week of year from current date) from EntityOfBasics e")
|
||||
session.createQuery("select extract(week of year from current_date) from EntityOfBasics e")
|
||||
.list();
|
||||
|
||||
// I really don't like this "separate word" approach - here, even moreso. The problem is the PR also defines a
|
||||
// `FIELD( temporalValue)` form which here, e.g., would mean this is a valid expression: `week of year( current date )` which is awful imo
|
||||
// session.createQuery("select extract(week of year from current date) from EntityOfBasics e")
|
||||
// .list();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isolated(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
session.createQuery("select extract(time from local_datetime), extract(date from local_datetime) from EntityOfBasics e")
|
||||
.list();
|
||||
}
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue