HHH-16185 Custom trunc/truncate implementation that handles both numeric and datetimes

This commit is contained in:
Marco Belladelli 2023-02-15 18:28:15 +01:00 committed by Christian Beikov
parent 821e31b481
commit 546d145d88
37 changed files with 913 additions and 369 deletions

View File

@ -800,13 +800,14 @@ include::{example-dir-hql}/HQLTest.java[tags=hql-nullif-example]
[[hql-functions-datetime]]
==== Functions for working with dates and times
There are two very important function for working with dates and times.
There are some very important functions for working with dates and times.
|===
| Special function | Purpose | Signature | JPA standard
| `extract()` | Extract a datetime field | `extract(field from x)` | ✓
| `format()` | Format a datetime as a string | `format(datetime as pattern)` | ✗
| `trunc()` or `truncate()` | Datetime truncation | `truncate(datetime, field)` | ✗
|===
[[hql-function-extract]]
@ -855,6 +856,16 @@ The syntax is `format(datetime as pattern)`, and the pattern must be written in
For a full list of `format()` pattern elements, see the Javadoc for https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/dialect/Dialect.html#appendDatetimeFormat[`Dialect#appendDatetimeFormat`].
[[hql-function-trunc-datetime]]
===== `trunc()` or `truncate()`
This function truncates a date, time, or datetime to the temporal unit specified by field.
The syntax is `truncate(datetime, field)`. Supported temporal units are: `year`, `month`, `day`, `hour`, `minute` or `second`.
Truncating a date, time or datetime value translates to obtaining a value of the same type in which all temporal units smaller than `field` have been pruned.
For hours, minutes and second this means setting them to `00`. For months and days, this means setting them to `01'.
[[hql-string-functions]]
==== Functions for working with strings

View File

@ -168,7 +168,7 @@ public class CacheDialect extends Dialect {
functionFactory.weekQuarter();
functionFactory.daynameMonthname();
functionFactory.toCharNumberDateTimestamp();
functionFactory.truncate();
functionFactory.trunc_truncate();
functionFactory.dayofweekmonthyear();
functionFactory.repeat_replicate();
functionFactory.datepartDatename();

View File

@ -45,7 +45,7 @@ import org.hibernate.dialect.SpannerDialect;
import org.hibernate.dialect.TimeZoneSupport;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.FormatFunction;
import org.hibernate.dialect.function.PostgreSQLTruncRoundFunction;
import org.hibernate.dialect.function.PostgreSQLTruncFunction;
import org.hibernate.dialect.identity.CockroachDBIdentityColumnSupport;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.pagination.LimitHandler;
@ -435,7 +435,11 @@ public class CockroachLegacyDialect extends Dialect {
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
functionContributions.getFunctionRegistry().register(
"trunc", new PostgreSQLTruncRoundFunction( "trunc", getVersion().isSameOrAfter( 22, 2 ) )
"trunc",
new PostgreSQLTruncFunction(
getVersion().isSameOrAfter( 22, 2 ),
functionContributions.getTypeConfiguration()
)
);
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
}

View File

@ -295,8 +295,6 @@ public class DB2LegacyDialect extends Dialect {
functionFactory.octetLength();
functionFactory.ascii();
functionFactory.char_chr();
functionFactory.trunc();
// functionFactory.truncate();
functionFactory.insert();
functionFactory.characterLength_length( SqlAstNodeRenderingMode.DEFAULT );
functionFactory.stddev();
@ -312,6 +310,7 @@ public class DB2LegacyDialect extends Dialect {
functionFactory.varPopSamp();
functionFactory.varianceSamp();
functionFactory.dateTrunc();
functionFactory.trunc_dateTrunc();
}
else {
// Before version 11, the position function required the use of the code units
@ -327,7 +326,7 @@ public class DB2LegacyDialect extends Dialect {
functionFactory.stddevSamp_sumCount();
functionContributions.getFunctionRegistry().registerAlternateKey( "var_pop", "variance" );
functionFactory.varSamp_sumCount();
functionFactory.dateTrunc_trunc();
functionFactory.trunc_dateTrunc_trunc();
}
functionFactory.addYearsMonthsDaysHoursMinutesSeconds();

View File

@ -309,8 +309,7 @@ public class H2LegacyDialect extends Dialect {
if ( useLocalTime ) {
functionFactory.localtimeLocaltimestamp();
}
functionFactory.trunc();
// functionFactory.truncate();
functionFactory.trunc_dateTrunc();
functionFactory.dateTrunc();
functionFactory.bitLength();
functionFactory.octetLength();

View File

@ -199,9 +199,7 @@ public class HSQLLegacyDialect extends Dialect {
functionFactory.degrees();
functionFactory.log10();
functionFactory.rand();
functionFactory.trunc();
// functionFactory.truncate();
functionFactory.dateTrunc_trunc();
functionFactory.trunc_dateTrunc_trunc();
functionFactory.pi();
functionFactory.soundex();
functionFactory.reverse();

View File

@ -251,8 +251,8 @@ public class IngresDialect extends Dialect {
functionFactory.octetLength();
functionFactory.repeat();
functionFactory.trim2();
functionFactory.trunc();
// functionFactory.truncate();
functionFactory.dateTrunc();
functionFactory.trunc_dateTrunc();
functionFactory.initcap();
functionFactory.yearMonthDay();
functionFactory.hourMinuteSecond();
@ -269,7 +269,6 @@ public class IngresDialect extends Dialect {
functionFactory.sysdate();
functionFactory.position();
functionFactory.format_dateFormat();
functionFactory.dateTrunc();
functionFactory.bitLength_pattern( "octet_length(hex(?1))*4" );
final BasicType<Integer> integerType = functionContributions.getTypeConfiguration().getBasicTypeRegistry()

View File

@ -158,7 +158,7 @@ public class MimerSQLDialect extends Dialect {
functionFactory.soundex();
functionFactory.octetLength();
functionFactory.bitLength();
functionFactory.truncate();
functionFactory.trunc_truncate();
functionFactory.repeat();
functionFactory.pad_repeat();
functionFactory.dayofweekmonthyear();

View File

@ -562,7 +562,6 @@ public class MySQLLegacyDialect extends Dialect {
functionFactory.position();
functionFactory.nowCurdateCurtime();
functionFactory.trunc_truncate();
functionFactory.dateTrunc_format( "str_to_date", false );
functionFactory.insert();
functionFactory.bitandorxornot_operator();
functionFactory.bitAndOr();

View File

@ -38,6 +38,7 @@ import org.hibernate.dialect.aggregate.OracleAggregateSupport;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.ModeStatsModeEmulation;
import org.hibernate.dialect.function.NvlCoalesceEmulation;
import org.hibernate.dialect.function.OracleTruncFunction;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.Oracle12cIdentityColumnSupport;
import org.hibernate.dialect.pagination.LegacyOracleLimitHandler;
@ -185,7 +186,6 @@ public class OracleLegacyDialect extends Dialect {
functionFactory.cosh();
functionFactory.sinh();
functionFactory.tanh();
functionFactory.trunc();
functionFactory.log();
functionFactory.log10_log();
functionFactory.soundex();
@ -255,6 +255,11 @@ public class OracleLegacyDialect extends Dialect {
"mode",
new ModeStatsModeEmulation( typeConfiguration )
);
functionContributions.getFunctionRegistry().register(
"trunc",
new OracleTruncFunction( functionContributions.getTypeConfiguration() )
);
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
}
@Override

View File

@ -49,6 +49,7 @@ import org.hibernate.dialect.aggregate.AggregateSupport;
import org.hibernate.dialect.aggregate.PostgreSQLAggregateSupport;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.PostgreSQLMinMaxFunction;
import org.hibernate.dialect.function.PostgreSQLTruncFunction;
import org.hibernate.dialect.function.PostgreSQLTruncRoundFunction;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.PostgreSQLIdentityColumnSupport;
@ -541,8 +542,6 @@ public class PostgreSQLLegacyDialect extends Dialect {
CommonFunctionFactory functionFactory = new CommonFunctionFactory(functionContributions);
functionFactory.round_roundFloor(); //Postgres round(x,n) does not accept double
functionFactory.trunc_truncFloor();
functionFactory.cot();
functionFactory.radians();
functionFactory.degrees();
@ -571,7 +570,6 @@ public class PostgreSQLLegacyDialect extends Dialect {
functionFactory.toCharNumberDateTimestamp();
functionFactory.concat_pipeOperator( "convert_from(lo_get(?1),pg_client_encoding())" );
functionFactory.localtimeLocaltimestamp();
functionFactory.dateTrunc();
functionFactory.length_characterLength_pattern( "length(lo_get(?1),pg_client_encoding())" );
functionFactory.bitLength_pattern( "bit_length(?1)", "length(lo_get(?1))*8" );
functionFactory.octetLength_pattern( "octet_length(?1)", "length(lo_get(?1))" );
@ -613,9 +611,11 @@ public class PostgreSQLLegacyDialect extends Dialect {
"round", new PostgreSQLTruncRoundFunction( "round", true )
);
functionContributions.getFunctionRegistry().register(
"trunc", new PostgreSQLTruncRoundFunction( "trunc", true )
"trunc",
new PostgreSQLTruncFunction( true, functionContributions.getTypeConfiguration() )
);
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
functionFactory.dateTrunc();
}
/**

View File

@ -21,6 +21,7 @@ import org.hibernate.dialect.TimeZoneSupport;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.CountFunction;
import org.hibernate.dialect.function.SQLServerFormatEmulation;
import org.hibernate.dialect.function.SqlServerConvertTruncFunction;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.SQLServerIdentityColumnSupport;
import org.hibernate.dialect.pagination.LimitHandler;
@ -303,7 +304,6 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
functionFactory.log_log();
functionFactory.trunc_round();
functionFactory.round_round();
functionFactory.everyAny_minMaxIif();
functionFactory.octetLength_pattern( "datalength(?1)" );
@ -367,9 +367,14 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
if ( getVersion().isSameOrAfter( 16 ) ) {
functionFactory.leastGreatest();
functionFactory.dateTrunc_datetrunc();
functionFactory.trunc_round_datetrunc();
}
else {
functionFactory.dateTrunc_format( "convert", false );
functionContributions.getFunctionRegistry().register(
"trunc",
new SqlServerConvertTruncFunction( functionContributions.getTypeConfiguration() )
);
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
}
}

View File

@ -18,6 +18,7 @@ import org.hibernate.dialect.NationalizationSupport;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.CountFunction;
import org.hibernate.dialect.function.IntegralTimestampaddFunction;
import org.hibernate.dialect.function.SybaseTruncFunction;
import org.hibernate.dialect.unique.SkipNullableUniqueDelegate;
import org.hibernate.dialect.unique.UniqueDelegate;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
@ -216,9 +217,7 @@ public class SybaseLegacyDialect extends AbstractTransactSQLDialect {
functionFactory.varPopSamp_varp();
functionFactory.stddevPopSamp();
functionFactory.varPopSamp();
functionFactory.trunc_floorPower();
functionFactory.round_round();
functionFactory.dateTrunc_format( "convert", true );
// For SQL-Server we need to cast certain arguments to varchar(16384) to be able to concat them
functionContributions.getFunctionRegistry().register(
@ -249,6 +248,11 @@ public class SybaseLegacyDialect extends AbstractTransactSQLDialect {
functionContributions.getFunctionRegistry().register( "timestampadd",
new IntegralTimestampaddFunction( this, functionContributions.getTypeConfiguration() ) );
functionContributions.getFunctionRegistry().register(
"trunc",
new SybaseTruncFunction( functionContributions.getTypeConfiguration() )
);
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
}
@Override

View File

@ -290,6 +290,7 @@ TO : [tT] [oO];
TRAILING : [tT] [rR] [aA] [iI] [lL] [iI] [nN] [gG];
TREAT : [tT] [rR] [eE] [aA] [tT];
TRIM : [tT] [rR] [iI] [mM];
TRUNC : [tT] [rR] [uU] [nN] [cC];
TRUNCATE : [tT] [rR] [uU] [nN] [cC] [aA] [tT] [eE];
TYPE : [tT] [yY] [pP] [eE];
UNBOUNDED : [uU] [nN] [bB] [oO] [uU] [nN] [dD] [eE] [dD];

View File

@ -1245,6 +1245,7 @@ frameExclusion
standardFunction
: castFunction
| extractFunction
| truncFunction
| formatFunction
| collateFunction
| substringFunction
@ -1452,6 +1453,13 @@ extractFunction
| datetimeField LEFT_PAREN expression RIGHT_PAREN
;
/**
* The 'trunc()' function for truncating both numeric and datetime values
*/
truncFunction
: (TRUNC | TRUNCATE) LEFT_PAREN expression (COMMA (datetimeField | expression))? RIGHT_PAREN
;
/**
* A field that may be extracted from a date, time, or datetime
*/
@ -1688,6 +1696,7 @@ rollup
| TRAILING
| TREAT
| TRIM
| TRUNC
| TRUNCATE
| TYPE
| UNBOUNDED

View File

@ -398,7 +398,6 @@ public abstract class AbstractHANADialect extends Dialect {
functionFactory.sinh();
functionFactory.tanh();
functionFactory.trunc_roundMode();
functionFactory.dateTrunc_format( "to_date", false );
functionFactory.log10_log();
functionFactory.log();
functionFactory.bitand();

View File

@ -27,7 +27,7 @@ import org.hibernate.boot.model.FunctionContributions;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.FormatFunction;
import org.hibernate.dialect.function.PostgreSQLTruncRoundFunction;
import org.hibernate.dialect.function.PostgreSQLTruncFunction;
import org.hibernate.dialect.identity.CockroachDBIdentityColumnSupport;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.pagination.LimitHandler;
@ -430,7 +430,11 @@ public class CockroachDialect extends Dialect {
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
functionContributions.getFunctionRegistry().register(
"trunc", new PostgreSQLTruncRoundFunction( "trunc", getVersion().isSameOrAfter( 22, 2 ) )
"trunc",
new PostgreSQLTruncFunction(
getVersion().isSameOrAfter( 22, 2 ),
functionContributions.getTypeConfiguration()
)
);
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
}

View File

@ -289,8 +289,6 @@ public class DB2Dialect extends Dialect {
functionFactory.octetLength();
functionFactory.ascii();
functionFactory.char_chr();
functionFactory.trunc();
// functionFactory.truncate();
functionFactory.insert();
functionFactory.characterLength_length( SqlAstNodeRenderingMode.DEFAULT );
functionFactory.stddev();
@ -306,6 +304,7 @@ public class DB2Dialect extends Dialect {
functionFactory.varPopSamp();
functionFactory.varianceSamp();
functionFactory.dateTrunc();
functionFactory.trunc_dateTrunc();
}
else {
// Before version 11, the position function required the use of the code units
@ -321,7 +320,7 @@ public class DB2Dialect extends Dialect {
functionFactory.stddevSamp_sumCount();
functionContributions.getFunctionRegistry().registerAlternateKey( "var_pop", "variance" );
functionFactory.varSamp_sumCount();
functionFactory.dateTrunc_trunc();
functionFactory.trunc_dateTrunc_trunc();
}
functionFactory.addYearsMonthsDaysHoursMinutesSeconds();

View File

@ -286,8 +286,7 @@ public class H2Dialect extends Dialect {
if ( useLocalTime ) {
functionFactory.localtimeLocaltimestamp();
}
functionFactory.trunc();
// functionFactory.truncate();
functionFactory.trunc_dateTrunc();
functionFactory.dateTrunc();
functionFactory.bitLength();
functionFactory.octetLength();

View File

@ -140,9 +140,7 @@ public class HSQLDialect extends Dialect {
functionFactory.degrees();
functionFactory.log10();
functionFactory.rand();
functionFactory.trunc();
// functionFactory.truncate();
functionFactory.dateTrunc_trunc();
functionFactory.trunc_dateTrunc_trunc();
functionFactory.pi();
functionFactory.soundex();
functionFactory.reverse();

View File

@ -561,7 +561,6 @@ public class MySQLDialect extends Dialect {
functionFactory.position();
functionFactory.nowCurdateCurtime();
functionFactory.trunc_truncate();
functionFactory.dateTrunc_format( "str_to_date", false );
functionFactory.insert();
functionFactory.bitandorxornot_operator();
functionFactory.bitAndOr();

View File

@ -24,6 +24,7 @@ import org.hibernate.dialect.aggregate.AggregateSupport;
import org.hibernate.dialect.aggregate.OracleAggregateSupport;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.ModeStatsModeEmulation;
import org.hibernate.dialect.function.OracleTruncFunction;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.Oracle12cIdentityColumnSupport;
import org.hibernate.dialect.pagination.LegacyOracleLimitHandler;
@ -222,7 +223,6 @@ public class OracleDialect extends Dialect {
functionFactory.cosh();
functionFactory.sinh();
functionFactory.tanh();
functionFactory.trunc();
functionFactory.log();
functionFactory.log10_log();
functionFactory.soundex();
@ -236,7 +236,6 @@ public class OracleDialect extends Dialect {
functionFactory.bitand();
functionFactory.lastDay();
functionFactory.toCharNumberDateTimestamp();
functionFactory.dateTrunc_trunc();
functionFactory.ceiling_ceil();
functionFactory.concat_pipeOperator();
functionFactory.rownumRowid();
@ -288,6 +287,11 @@ public class OracleDialect extends Dialect {
"mode",
new ModeStatsModeEmulation( typeConfiguration )
);
functionContributions.getFunctionRegistry().register(
"trunc",
new OracleTruncFunction( functionContributions.getTypeConfiguration() )
);
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
}
@Override

View File

@ -29,6 +29,7 @@ import org.hibernate.dialect.aggregate.AggregateSupport;
import org.hibernate.dialect.aggregate.PostgreSQLAggregateSupport;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.PostgreSQLMinMaxFunction;
import org.hibernate.dialect.function.PostgreSQLTruncFunction;
import org.hibernate.dialect.function.PostgreSQLTruncRoundFunction;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.PostgreSQLIdentityColumnSupport;
@ -564,7 +565,6 @@ public class PostgreSQLDialect extends Dialect {
functionFactory.toCharNumberDateTimestamp();
functionFactory.concat_pipeOperator( "convert_from(lo_get(?1),pg_client_encoding())" );
functionFactory.localtimeLocaltimestamp();
functionFactory.dateTrunc();
functionFactory.length_characterLength_pattern( "length(lo_get(?1),pg_client_encoding())" );
functionFactory.bitLength_pattern( "bit_length(?1)", "length(lo_get(?1))*8" );
functionFactory.octetLength_pattern( "octet_length(?1)", "length(lo_get(?1))" );
@ -604,9 +604,11 @@ public class PostgreSQLDialect extends Dialect {
"round", new PostgreSQLTruncRoundFunction( "round", true )
);
functionContributions.getFunctionRegistry().register(
"trunc", new PostgreSQLTruncRoundFunction( "trunc", true )
"trunc",
new PostgreSQLTruncFunction( true, functionContributions.getTypeConfiguration() )
);
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
functionFactory.dateTrunc();
}
/**

View File

@ -28,6 +28,7 @@ import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.CountFunction;
import org.hibernate.dialect.function.SQLServerFormatEmulation;
import org.hibernate.dialect.function.SqlServerConvertTruncFunction;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.SQLServerIdentityColumnSupport;
import org.hibernate.dialect.pagination.LimitHandler;
@ -309,7 +310,6 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
functionFactory.log_log();
functionFactory.trunc_round();
functionFactory.round_round();
functionFactory.everyAny_minMaxIif();
functionFactory.octetLength_pattern( "datalength(?1)" );
@ -371,9 +371,14 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
if ( getVersion().isSameOrAfter( 16 ) ) {
functionFactory.leastGreatest();
functionFactory.dateTrunc_datetrunc();
functionFactory.trunc_round_datetrunc();
}
else {
functionFactory.dateTrunc_format( "convert", false );
functionContributions.getFunctionRegistry().register(
"trunc",
new SqlServerConvertTruncFunction( functionContributions.getTypeConfiguration() )
);
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
}
}

View File

@ -15,6 +15,7 @@ import org.hibernate.boot.model.TypeContributions;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.CountFunction;
import org.hibernate.dialect.function.IntegralTimestampaddFunction;
import org.hibernate.dialect.function.SybaseTruncFunction;
import org.hibernate.dialect.unique.SkipNullableUniqueDelegate;
import org.hibernate.dialect.unique.UniqueDelegate;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
@ -220,9 +221,7 @@ public class SybaseDialect extends AbstractTransactSQLDialect {
functionFactory.varPopSamp_varp();
functionFactory.stddevPopSamp();
functionFactory.varPopSamp();
functionFactory.trunc_floorPower();
functionFactory.round_round();
functionFactory.dateTrunc_format( "convert", true );
// For SQL-Server we need to cast certain arguments to varchar(16384) to be able to concat them
functionContributions.getFunctionRegistry().register(
@ -253,6 +252,11 @@ public class SybaseDialect extends AbstractTransactSQLDialect {
functionContributions.getFunctionRegistry().register( "timestampadd",
new IntegralTimestampaddFunction( this, functionContributions.getTypeConfiguration() ) );
functionContributions.getFunctionRegistry().register(
"trunc",
new SybaseTruncFunction( functionContributions.getTypeConfiguration() )
);
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
}
@Override

View File

@ -259,123 +259,67 @@ public class CommonFunctionFactory {
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// basic math functions
// numeric and datetime truncation
private void trunc(
String truncPattern,
String twoArgTruncPattern,
TruncFunction.DatetimeTrunc datetimeTrunc,
String toDateFunction) {
functionRegistry.register(
"trunc",
new TruncFunction( truncPattern, twoArgTruncPattern, datetimeTrunc, toDateFunction, typeConfiguration )
);
functionRegistry.registerAlternateKey( "truncate", "trunc" );
}
private void trunc(TruncFunction.DatetimeTrunc datetimeTrunc) {
trunc( "trunc(?1)", "trunc(?1,?2)", datetimeTrunc, null );
}
public void trunc() {
functionRegistry.namedDescriptorBuilder( "trunc" )
.setReturnTypeResolver( useArgType( 1 ) )
.setArgumentCountBetween( 1, 2 )
.setParameterTypes(NUMERIC, INTEGER)
.setArgumentListSignature( "(NUMERIC number[, INTEGER places])" )
.register();
functionRegistry.registerAlternateKey( "truncate", "trunc" );
trunc( null );
}
public void trunc_dateTrunc() {
trunc( TruncFunction.DatetimeTrunc.DATE_TRUNC );
}
public void trunc_dateTrunc_trunc() {
trunc( TruncFunction.DatetimeTrunc.TRUNC );
}
/**
* MySQL
*/
public void trunc_truncate() {
functionRegistry.registerUnaryBinaryPattern(
"trunc",
"truncate(?1,0)",
"truncate(?1,?2)",
NUMERIC, INTEGER,
typeConfiguration
).setArgumentListSignature( "(NUMERIC number[, INTEGER places])" );
functionRegistry.registerAlternateKey( "truncate", "trunc" );
trunc( "truncate(?1,0)", "truncate(?1,?2)", TruncFunction.DatetimeTrunc.FORMAT, "str_to_date" );
}
/**
* SQL Server
* SQL Server >= 16
*/
public void trunc_round() {
functionRegistry.registerUnaryBinaryPattern(
"trunc",
"round(?1,0,1)",
"round(?1,?2,1)",
NUMERIC, INTEGER,
typeConfiguration
).setArgumentListSignature( "(NUMERIC number[, INTEGER places])" );
functionRegistry.registerAlternateKey( "truncate", "trunc" );
}
/**
* Sybase
*/
public void trunc_floorPower() {
functionRegistry.registerUnaryBinaryPattern(
"trunc",
"sign(?1)*floor(abs(?1))",
"sign(?1)*floor(abs(?1)*power(10,?2))/power(10,?2)",
NUMERIC, INTEGER,
typeConfiguration
).setArgumentListSignature( "(NUMERIC number[, INTEGER places])" );
functionRegistry.registerAlternateKey( "truncate", "trunc" );
}
/**
* PostgreSQL (only works if the second arg is constant, as it almost always is)
*/
public void trunc_truncFloor() {
functionRegistry.registerUnaryBinaryPattern(
"trunc",
"trunc(?1)",
"sign(?1)*floor(abs(?1)*1e?2)/1e?2",
NUMERIC, INTEGER,
typeConfiguration
).setArgumentListSignature( "(NUMERIC number[, INTEGER places])" );
functionRegistry.registerAlternateKey( "truncate", "trunc" );
public void trunc_round_datetrunc() {
trunc( "round(?1,0,1)", "round(?1,?2,1)", TruncFunction.DatetimeTrunc.DATETRUNC, "convert" );
}
/**
* Derby (only works if the second arg is constant, as it almost always is)
*/
public void trunc_floor() {
functionRegistry.registerUnaryBinaryPattern(
"trunc",
"sign(?1)*floor(abs(?1))",
"sign(?1)*floor(abs(?1)*1e?2)/1e?2",
NUMERIC, INTEGER,
typeConfiguration
).setArgumentListSignature( "(NUMERIC number[, INTEGER places])" );
functionRegistry.registerAlternateKey( "truncate", "trunc" );
}
public void truncate() {
functionRegistry.namedDescriptorBuilder( "truncate" )
.setExactArgumentCount( 2 ) //some databases allow 1 arg but in these it's a synonym for trunc()
.setParameterTypes(NUMERIC, INTEGER)
.setInvariantType(doubleType)
.setArgumentListSignature( "(NUMERIC number, INTEGER places)" )
.register();
}
/**
* SQL Server
*/
public void truncate_round() {
functionRegistry.patternDescriptorBuilder( "truncate", "round(?1,?2,1)" )
.setExactArgumentCount( 2 )
.setParameterTypes(NUMERIC, INTEGER)
.setInvariantType(doubleType)
.setArgumentListSignature( "(NUMERIC number, INTEGER places)" )
.register();
trunc( "sign(?1)*floor(abs(?1))", "sign(?1)*floor(abs(?1)*1e?2)/1e?2", null, null );
}
/**
* SAP HANA
*/
public void trunc_roundMode() {
functionRegistry.registerUnaryBinaryPattern(
"trunc",
"round(?1,0,round_down)",
"round(?1,?2,round_down)",
NUMERIC, INTEGER,
typeConfiguration
).setArgumentListSignature( "(NUMERIC number[, INTEGER places])" );
functionRegistry.registerAlternateKey( "truncate", "trunc" );
trunc( "round(?1,0,round_down)", "round(?1,?2,round_down)", TruncFunction.DatetimeTrunc.FORMAT, "to_date" );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// basic math functions
/**
* Returns double between 0.0 and 1.0. First call may specify a seed value.
*/
@ -2268,7 +2212,6 @@ public class CommonFunctionFactory {
NUMERIC, INTEGER,
typeConfiguration
).setArgumentListSignature( "(NUMERIC number[, INTEGER places])" );
functionRegistry.registerAlternateKey( "truncate", "trunc" );
}
/**
@ -2558,32 +2501,27 @@ public class CommonFunctionFactory {
.register();
}
/**
* H2, DB2 and PostgreSQL native date_trunc() function
*/
public void dateTrunc() {
functionRegistry.patternDescriptorBuilder( "date_trunc", "date_trunc('?1',?2)" )
functionRegistry.patternDescriptorBuilder( "date_trunc", "date_trunc(?1,?2)" )
.setReturnTypeResolver( useArgType( 2 ) )
.setExactArgumentCount( 2 )
.setParameterTypes( TEMPORAL_UNIT, TEMPORAL )
.setArgumentListSignature( "(TEMPORAL_UNIT field, TEMPORAL datetime)" )
.setParameterTypes( STRING, TEMPORAL )
.setArgumentListSignature( "(STRING field, TEMPORAL datetime)" )
.register();
}
/**
* SQLServer native datetrunc() function
*/
public void dateTrunc_datetrunc() {
functionRegistry.patternDescriptorBuilder( "date_trunc", "datetrunc(?1,?2)" )
functionRegistry.patternDescriptorBuilder( "datetrunc", "datetrunc(?1,?2)" )
.setReturnTypeResolver( useArgType( 2 ) )
.setExactArgumentCount( 2 )
.setParameterTypes( TEMPORAL_UNIT, TEMPORAL )
.setArgumentListSignature( "(TEMPORAL_UNIT field, TEMPORAL datetime)" )
.register();
}
public void dateTrunc_trunc() {
functionRegistry.register( "date_trunc", new DateTruncTrunc( typeConfiguration ) );
}
public void dateTrunc_format(String toDateFunction, boolean useConvertToFormat) {
functionRegistry.register(
"date_trunc",
new DateTruncEmulation( toDateFunction, useConvertToFormat, typeConfiguration )
);
}
}

View File

@ -6,7 +6,6 @@
*/
package org.hibernate.dialect.function;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.query.ReturnableType;
@ -21,8 +20,8 @@ import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.SqmDurationUnit;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.expression.SqmExtractUnit;
import org.hibernate.query.sqm.tree.expression.SqmFormat;
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.sql.ast.SqlAstTranslator;
@ -35,25 +34,24 @@ import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TEM
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TEMPORAL_UNIT;
/**
* Emulation of {@code datetrunc} function that leverages
* Emulation of {@code trunc(datetime, temporal_unit)} function that leverages
* formatting the datetime to string and back to truncate it
*
* @author Marco Belladelli
*/
public class DateTruncEmulation extends AbstractSqmFunctionDescriptor implements FunctionRenderingSupport {
private final String toDateFunction;
protected final String toDateFunction;
private final SqmFormat sqmFormat;
private final boolean useConvertToFormat;
public DateTruncEmulation(String toDateFunction, boolean useConvertToFormat, TypeConfiguration typeConfiguration) {
protected DateTruncEmulation(String toDateFunction, TypeConfiguration typeConfiguration) {
super(
"date_trunc",
new ArgumentTypesValidator( StandardArgumentsValidators.exactly( 2 ), TEMPORAL_UNIT, TEMPORAL ),
StandardFunctionReturnTypeResolvers.useArgType( 2 ),
StandardFunctionArgumentTypeResolvers.invariant( typeConfiguration, TEMPORAL_UNIT, TEMPORAL )
"trunc",
new ArgumentTypesValidator( StandardArgumentsValidators.exactly( 2 ), TEMPORAL, TEMPORAL_UNIT ),
StandardFunctionReturnTypeResolvers.useArgType( 1 ),
StandardFunctionArgumentTypeResolvers.invariant( typeConfiguration, TEMPORAL, TEMPORAL_UNIT )
);
this.toDateFunction = toDateFunction;
this.useConvertToFormat = useConvertToFormat;
this.sqmFormat = new SqmFormat( "yyyy-MM-dd HH:mm:ss", typeConfiguration.getBasicTypeForJavaType( String.class ), null );
}
@Override
@ -63,28 +61,9 @@ public class DateTruncEmulation extends AbstractSqmFunctionDescriptor implements
SqlAstTranslator<?> walker) {
sqlAppender.appendSql( toDateFunction );
sqlAppender.append( '(' );
if ( !useConvertToFormat ) {
if ( toDateFunction.equalsIgnoreCase( "convert" ) ) {
sqlAppender.append( "datetime," );
sqlAstArguments.get( 0 ).accept( walker );
}
else {
sqlAstArguments.get( 0 ).accept( walker );
sqlAppender.append( ',' );
sqlAstArguments.get( 1 ).accept( walker );
}
}
else {
// custom implementation that uses convert instead of format for Sybase
// see: https://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.dc36271.1600/doc/html/san1393050437990.html
sqlAppender.append( "datetime,substring(convert(varchar," );
sqlAstArguments.get( 1 ).accept( walker );
sqlAppender.append( ",21),1,17-len(" );
sqlAstArguments.get( 0 ).accept( walker );
sqlAppender.append( "))+" );
sqlAstArguments.get( 0 ).accept( walker );
sqlAppender.append( ",21" );
}
sqlAstArguments.get( 1 ).accept( walker );
sqlAppender.append( ',' );
sqlAstArguments.get( 2 ).accept( walker );
sqlAppender.append( ')' );
}
@ -95,7 +74,7 @@ public class DateTruncEmulation extends AbstractSqmFunctionDescriptor implements
QueryEngine queryEngine,
TypeConfiguration typeConfiguration) {
final NodeBuilder nodeBuilder = queryEngine.getCriteriaBuilder();
final TemporalUnit temporalUnit = ( (SqmDurationUnit<?>) arguments.get( 0 ) ).getUnit();
final TemporalUnit temporalUnit = ( (SqmExtractUnit<?>) arguments.get( 1 ) ).getUnit();
final String pattern;
final String literal;
switch ( temporalUnit ) {
@ -126,18 +105,31 @@ public class DateTruncEmulation extends AbstractSqmFunctionDescriptor implements
default:
throw new UnsupportedOperationException( "Temporal unit not supported [" + temporalUnit + "]" );
}
final SqmTypedNode<?> datetime = arguments.get( 1 );
final List<SqmTypedNode<?>> args = new ArrayList<>( 2 );
if ( !useConvertToFormat ) {
// use standard format function
final SqmExpression<?> formatExpression = queryEngine.getSqmFunctionRegistry()
.findFunctionDescriptor( "format" )
final SqmTypedNode<?> datetime = arguments.get( 0 );
final SqmExpression<?> formatExpression = queryEngine.getSqmFunctionRegistry()
.findFunctionDescriptor( "format" )
.generateSqmExpression(
asList(
datetime,
new SqmFormat(
pattern,
typeConfiguration.getBasicTypeForJavaType( String.class ),
nodeBuilder
)
),
null,
queryEngine,
typeConfiguration
);
final SqmExpression<?> formattedDatetime;
if ( literal != null ) {
formattedDatetime = queryEngine.getSqmFunctionRegistry()
.findFunctionDescriptor( "concat" )
.generateSqmExpression(
asList(
datetime,
new SqmFormat(
pattern,
formatExpression,
new SqmLiteral<>(
literal,
typeConfiguration.getBasicTypeForJavaType( String.class ),
nodeBuilder
)
@ -146,51 +138,21 @@ public class DateTruncEmulation extends AbstractSqmFunctionDescriptor implements
queryEngine,
typeConfiguration
);
final SqmExpression<?> formattedDatetime;
if ( literal != null ) {
formattedDatetime = queryEngine.getSqmFunctionRegistry()
.findFunctionDescriptor( "concat" )
.generateSqmExpression(
asList(
formatExpression,
new SqmLiteral<>(
literal,
typeConfiguration.getBasicTypeForJavaType( String.class ),
nodeBuilder
)
),
null,
queryEngine,
typeConfiguration
);
}
else {
formattedDatetime = formatExpression;
}
args.add( formattedDatetime );
args.add( new SqmFormat(
"yyyy-MM-dd HH:mm:ss",
typeConfiguration.getBasicTypeForJavaType( String.class ),
nodeBuilder
) );
}
else {
args.add( new SqmLiteral<>(
literal != null ? literal.replace( "-", "/" ) : "",
typeConfiguration.getBasicTypeForJavaType( String.class ),
nodeBuilder
) );
args.add( datetime );
formattedDatetime = formatExpression;
}
return new SelfRenderingSqmFunction<>(
this,
this,
args,
// the first argument is needed for SybaseDateTruncEmulation
asList( datetime, formattedDatetime, sqmFormat ),
impliedResultType,
getArgumentsValidator(),
null,
getReturnTypeResolver(),
nodeBuilder,
"date_trunc"
getName()
);
}
}

View File

@ -1,127 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.dialect.function;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.dialect.OracleDialect;
import org.hibernate.query.ReturnableType;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.TemporalUnit;
import org.hibernate.query.sqm.function.AbstractSqmFunctionDescriptor;
import org.hibernate.query.sqm.function.FunctionRenderingSupport;
import org.hibernate.query.sqm.function.SelfRenderingSqmFunction;
import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator;
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.SqmDurationUnit;
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TEMPORAL;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TEMPORAL_UNIT;
/**
* Trunc function which converts the {@link TemporalUnit}
* to the format required for {@code trunc()}
*
* @author Marco Belladelli
*/
public class DateTruncTrunc extends AbstractSqmFunctionDescriptor implements FunctionRenderingSupport {
public DateTruncTrunc(TypeConfiguration typeConfiguration) {
super(
"date_trunc",
new ArgumentTypesValidator( StandardArgumentsValidators.exactly( 2 ), TEMPORAL_UNIT, TEMPORAL ),
StandardFunctionReturnTypeResolvers.useArgType( 2 ),
StandardFunctionArgumentTypeResolvers.invariant( typeConfiguration, TEMPORAL_UNIT, TEMPORAL )
);
}
@Override
public void render(
SqlAppender sqlAppender,
List<? extends SqlAstNode> sqlAstArguments,
SqlAstTranslator<?> walker) {
sqlAppender.appendSql( "trunc(" );
sqlAstArguments.get( 1 ).accept( walker );
sqlAppender.append( ',' );
sqlAstArguments.get( 0 ).accept( walker );
sqlAppender.append( ')' );
}
@Override
protected <T> SelfRenderingSqmFunction<T> generateSqmFunctionExpression(
List<? extends SqmTypedNode<?>> arguments,
ReturnableType<T> impliedResultType,
QueryEngine queryEngine,
TypeConfiguration typeConfiguration) {
final NodeBuilder nodeBuilder = queryEngine.getCriteriaBuilder();
final TemporalUnit temporalUnit = ( (SqmDurationUnit<?>) arguments.get( 0 ) ).getUnit();
final String pattern;
switch ( temporalUnit ) {
case YEAR:
pattern = "YYYY";
break;
case MONTH:
pattern = "MM";
break;
case WEEK:
pattern = "IW";
break;
case DAY:
pattern = "DD";
break;
case HOUR:
pattern = "HH";
break;
case MINUTE:
pattern = "MI";
break;
case SECOND:
if ( nodeBuilder.getSessionFactory().getJdbcServices().getDialect() instanceof OracleDialect ) {
// Oracle does not support truncating to seconds with the native function, use emulation
return new DateTruncEmulation( "to_date", false, typeConfiguration )
.generateSqmFunctionExpression(
arguments,
impliedResultType,
queryEngine,
typeConfiguration
);
}
pattern = "SS";
break;
default:
throw new UnsupportedOperationException( "Temporal unit not supported [" + temporalUnit + "]" );
}
final List<SqmTypedNode<?>> args = new ArrayList<>( 2 );
args.add( new SqmLiteral<>(
pattern,
typeConfiguration.getBasicTypeForJavaType( String.class ),
nodeBuilder
) );
args.add( arguments.get( 1 ) );
return new SelfRenderingSqmFunction<>(
this,
this,
args,
impliedResultType,
getArgumentsValidator(),
getReturnTypeResolver(),
nodeBuilder,
"date_trunc"
);
}
}

View File

@ -0,0 +1,114 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.dialect.function;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.query.ReturnableType;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.TemporalUnit;
import org.hibernate.query.sqm.function.FunctionRenderingSupport;
import org.hibernate.query.sqm.function.SelfRenderingSqmFunction;
import org.hibernate.query.sqm.produce.function.ArgumentsValidator;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.SqmExtractUnit;
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Custom {@link TruncFunction} for Oracle which uses emulation when truncating datetimes to seconds
*
* @author Marco Belladelli
*/
public class OracleTruncFunction extends TruncFunction {
private final DateTruncEmulation dateTruncEmulation;
public OracleTruncFunction(TypeConfiguration typeConfiguration) {
super(
"trunc(?1)",
"trunc(?1,?2)",
DatetimeTrunc.TRUNC,
null,
typeConfiguration
);
this.dateTruncEmulation = new DateTruncEmulation( "to_date", typeConfiguration );
}
@Override
protected <T> SelfRenderingSqmFunction<T> generateSqmFunctionExpression(
List<? extends SqmTypedNode<?>> arguments,
ReturnableType<T> impliedResultType,
QueryEngine queryEngine,
TypeConfiguration typeConfiguration) {
final NodeBuilder nodeBuilder = queryEngine.getCriteriaBuilder();
final List<SqmTypedNode<?>> args = new ArrayList<>( arguments );
final FunctionRenderingSupport renderingSupport;
final ArgumentsValidator argumentsValidator;
if ( arguments.size() == 2 && arguments.get( 1 ) instanceof SqmExtractUnit ) {
// datetime truncation
renderingSupport = datetimeRenderingSupport;
argumentsValidator = TruncArgumentsValidator.DATETIME_VALIDATOR;
// the trunc() function requires translating the temporal_unit to a format string
final TemporalUnit temporalUnit = ( (SqmExtractUnit<?>) arguments.get( 1 ) ).getUnit();
final String pattern;
switch ( temporalUnit ) {
case YEAR:
pattern = "YYYY";
break;
case MONTH:
pattern = "MM";
break;
case WEEK:
pattern = "IW";
break;
case DAY:
pattern = "DD";
break;
case HOUR:
pattern = "HH";
break;
case MINUTE:
pattern = "MI";
break;
case SECOND:
// Oracle does not support truncating to seconds with the native function, use emulation
return dateTruncEmulation.generateSqmFunctionExpression(
arguments,
impliedResultType,
queryEngine,
typeConfiguration
);
default:
throw new UnsupportedOperationException( "Temporal unit not supported [" + temporalUnit + "]" );
}
// replace temporal_unit parameter with translated string format literal
args.set( 1, new SqmLiteral<>(
pattern,
typeConfiguration.getBasicTypeForJavaType( String.class ),
nodeBuilder
) );
}
else {
// numeric truncation
renderingSupport = numericRenderingSupport;
argumentsValidator = TruncArgumentsValidator.NUMERIC_VALIDATOR;
}
return new SelfRenderingSqmFunction<>(
this,
renderingSupport,
args,
impliedResultType,
argumentsValidator,
getReturnTypeResolver(),
queryEngine.getCriteriaBuilder(),
getName()
);
}
}

View File

@ -0,0 +1,66 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.dialect.function;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.query.ReturnableType;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.function.SelfRenderingSqmFunction;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.SqmExtractUnit;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Custom {@link TruncFunction} for PostgreSQL which uses the dialect-specific function for numeric truncation
*
* @author Marco Belladelli
*/
public class PostgreSQLTruncFunction extends TruncFunction {
private final PostgreSQLTruncRoundFunction postgreSQLTruncRoundFunction;
public PostgreSQLTruncFunction(boolean supportsTwoArguments, TypeConfiguration typeConfiguration) {
super(
"trunc(?1)",
null,
DatetimeTrunc.DATE_TRUNC,
null,
typeConfiguration
);
this.postgreSQLTruncRoundFunction = new PostgreSQLTruncRoundFunction( "trunc", supportsTwoArguments );
}
@Override
protected <T> SelfRenderingSqmFunction<T> generateSqmFunctionExpression(
List<? extends SqmTypedNode<?>> arguments,
ReturnableType<T> impliedResultType,
QueryEngine queryEngine,
TypeConfiguration typeConfiguration) {
final List<SqmTypedNode<?>> args = new ArrayList<>( arguments );
if ( arguments.size() != 2 || !( arguments.get( 1 ) instanceof SqmExtractUnit ) ) {
// numeric truncation
return postgreSQLTruncRoundFunction.generateSqmFunctionExpression(
arguments,
impliedResultType,
queryEngine,
typeConfiguration
);
}
// datetime truncation
return new SelfRenderingSqmFunction<>(
this,
datetimeRenderingSupport,
args,
impliedResultType,
TruncArgumentsValidator.DATETIME_VALIDATOR,
getReturnTypeResolver(),
queryEngine.getCriteriaBuilder(),
getName()
);
}
}

View File

@ -8,16 +8,23 @@ package org.hibernate.dialect.function;
import java.util.List;
import org.hibernate.query.ReturnableType;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.function.AbstractSqmFunctionDescriptor;
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
import org.hibernate.query.sqm.function.FunctionRenderingSupport;
import org.hibernate.query.sqm.function.SelfRenderingSqmFunction;
import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator;
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.INTEGER;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.NUMERIC;
@ -41,7 +48,7 @@ import static org.hibernate.query.sqm.produce.function.FunctionParameterType.NUM
* @author Marco Belladelli
* @see <a href="https://www.postgresql.org/docs/current/functions-math.html">PostgreSQL documentation</a>
*/
public class PostgreSQLTruncRoundFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
public class PostgreSQLTruncRoundFunction extends AbstractSqmFunctionDescriptor implements FunctionRenderingSupport {
private final boolean supportsTwoArguments;
public PostgreSQLTruncRoundFunction(String name, boolean supportsTwoArguments) {
@ -96,4 +103,22 @@ public class PostgreSQLTruncRoundFunction extends AbstractSqmSelfRenderingFuncti
public String getArgumentListSignature() {
return "(NUMERIC number[, INTEGER places])";
}
@Override
protected <T> SelfRenderingSqmFunction<T> generateSqmFunctionExpression(
List<? extends SqmTypedNode<?>> arguments,
ReturnableType<T> impliedResultType,
QueryEngine queryEngine,
TypeConfiguration typeConfiguration) {
return new SelfRenderingSqmFunction<>(
this,
this,
arguments,
impliedResultType,
getArgumentsValidator(),
getReturnTypeResolver(),
queryEngine.getCriteriaBuilder(),
getName()
);
}
}

View File

@ -0,0 +1,92 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.dialect.function;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.query.ReturnableType;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.function.SelfRenderingSqmFunction;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.SqmExtractUnit;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Custom {@link TruncFunction} for SQL Server versions < 16 which uses the custom {@link DateTruncConvertEmulation}
*
* @author Marco Belladelli
*/
public class SqlServerConvertTruncFunction extends TruncFunction {
private final DateTruncConvertEmulation dateTruncEmulation;
public SqlServerConvertTruncFunction(TypeConfiguration typeConfiguration) {
super(
"round(?1,0,1)",
"round(?1,?2,1)",
null,
null,
typeConfiguration
);
this.dateTruncEmulation = new DateTruncConvertEmulation( typeConfiguration );
}
@Override
protected <T> SelfRenderingSqmFunction<T> generateSqmFunctionExpression(
List<? extends SqmTypedNode<?>> arguments,
ReturnableType<T> impliedResultType,
QueryEngine queryEngine,
TypeConfiguration typeConfiguration) {
final List<SqmTypedNode<?>> args = new ArrayList<>( arguments );
if ( arguments.size() == 2 && arguments.get( 1 ) instanceof SqmExtractUnit ) {
// datetime truncation
return dateTruncEmulation.generateSqmExpression(
arguments,
impliedResultType,
queryEngine,
typeConfiguration
);
}
// numeric truncation
return new SelfRenderingSqmFunction<>(
this,
numericRenderingSupport,
args,
impliedResultType,
TruncArgumentsValidator.NUMERIC_VALIDATOR,
getReturnTypeResolver(),
queryEngine.getCriteriaBuilder(),
getName()
);
}
/**
* Custom {@link DateTruncEmulation} that handles rendering when using the convert function to parse datetime strings
*
* @author Marco Belladelli
*/
private static class DateTruncConvertEmulation extends DateTruncEmulation {
public DateTruncConvertEmulation(TypeConfiguration typeConfiguration) {
super( "convert", typeConfiguration );
}
@Override
public void render(
SqlAppender sqlAppender,
List<? extends SqlAstNode> sqlAstArguments,
SqlAstTranslator<?> walker) {
sqlAppender.appendSql( toDateFunction );
sqlAppender.append( '(' );
sqlAppender.append( "datetime," );
sqlAstArguments.get( 1 ).accept( walker );
sqlAppender.append( ')' );
}
}
}

View File

@ -0,0 +1,154 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.dialect.function;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.query.ReturnableType;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.TemporalUnit;
import org.hibernate.query.sqm.function.SelfRenderingSqmFunction;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.SqmExtractUnit;
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.type.spi.TypeConfiguration;
import static java.util.Arrays.asList;
/**
* Custom {@link TruncFunction} for Sybase which uses a dialect-specific emulation function for datetimes
*
* @author Marco Belladelli
*/
public class SybaseTruncFunction extends TruncFunction {
private final SybaseDateTruncEmulation dateTruncEmulation;
public SybaseTruncFunction(TypeConfiguration typeConfiguration) {
super(
"sign(?1)*floor(abs(?1))",
"sign(?1)*floor(abs(?1)*power(10,?2))/power(10,?2)",
null,
null,
typeConfiguration
);
this.dateTruncEmulation = new SybaseDateTruncEmulation( "convert", typeConfiguration );
}
@Override
protected <T> SelfRenderingSqmFunction<T> generateSqmFunctionExpression(
List<? extends SqmTypedNode<?>> arguments,
ReturnableType<T> impliedResultType,
QueryEngine queryEngine,
TypeConfiguration typeConfiguration) {
final List<SqmTypedNode<?>> args = new ArrayList<>( arguments );
if ( arguments.size() == 2 && arguments.get( 1 ) instanceof SqmExtractUnit ) {
// datetime truncation
return dateTruncEmulation.generateSqmFunctionExpression(
arguments,
impliedResultType,
queryEngine,
typeConfiguration
);
}
// numeric truncation
return new SelfRenderingSqmFunction<>(
this,
numericRenderingSupport,
args,
impliedResultType,
TruncArgumentsValidator.NUMERIC_VALIDATOR,
getReturnTypeResolver(),
queryEngine.getCriteriaBuilder(),
getName()
);
}
/**
* Custom {@link DateTruncEmulation} that uses convert instead of format for Sybase
*
* @author Marco Belladelli
* @see <a href="https://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.dc36271.1600/doc/html/san1393050437990.html">Sybase Documentation</a>
*/
private static class SybaseDateTruncEmulation extends DateTruncEmulation {
public SybaseDateTruncEmulation(
String toDateFunction,
TypeConfiguration typeConfiguration) {
super( toDateFunction, typeConfiguration );
}
@Override
public void render(
SqlAppender sqlAppender,
List<? extends SqlAstNode> sqlAstArguments,
SqlAstTranslator<?> walker) {
sqlAppender.appendSql( toDateFunction );
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)" );
}
@Override
protected <T> SelfRenderingSqmFunction<T> generateSqmFunctionExpression(
List<? extends SqmTypedNode<?>> arguments,
ReturnableType<T> impliedResultType,
QueryEngine queryEngine,
TypeConfiguration typeConfiguration) {
final NodeBuilder nodeBuilder = queryEngine.getCriteriaBuilder();
final TemporalUnit temporalUnit = ( (SqmExtractUnit<?>) arguments.get( 1 ) ).getUnit();
final String literal;
switch ( temporalUnit ) {
case YEAR:
literal = "/01/01 00:00:00";
break;
case MONTH:
literal = "/01 00:00:00";
break;
case DAY:
literal = " 00:00:00";
break;
case HOUR:
literal = ":00:00";
break;
case MINUTE:
literal = ":00";
break;
case SECOND:
literal = "";
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 ),
impliedResultType,
null,
getReturnTypeResolver(),
nodeBuilder,
getName()
);
}
}
}

View File

@ -0,0 +1,227 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.dialect.function;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.query.ReturnableType;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.TemporalUnit;
import org.hibernate.query.sqm.function.AbstractSqmFunctionDescriptor;
import org.hibernate.query.sqm.function.FunctionRenderingSupport;
import org.hibernate.query.sqm.function.SelfRenderingSqmFunction;
import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator;
import org.hibernate.query.sqm.produce.function.ArgumentsValidator;
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.SqmExtractUnit;
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.NUMERIC;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TEMPORAL;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TEMPORAL_UNIT;
/**
* Custom function that manages both numeric and datetime truncation
*
* @author Marco Belladelli
*/
public class TruncFunction extends AbstractSqmFunctionDescriptor {
protected final TruncRenderingSupport numericRenderingSupport;
protected final TruncRenderingSupport datetimeRenderingSupport;
private final DatetimeTrunc datetimeTrunc;
private final DateTruncEmulation dateTruncEmulation;
public enum DatetimeTrunc {
DATE_TRUNC( "date_trunc('?2',?1)" ),
DATETRUNC( "datetrunc(?2,?1)" ),
TRUNC( "trunc(?1,?2)" ),
FORMAT( null );
private final String pattern;
DatetimeTrunc(String pattern) {
this.pattern = pattern;
}
public String getPattern() {
return pattern;
}
}
public TruncFunction(
String truncPattern,
String twoArgTruncPattern,
DatetimeTrunc datetimeTrunc,
String toDateFunction,
TypeConfiguration typeConfiguration) {
super(
"trunc",
new TruncArgumentsValidator(),
StandardFunctionReturnTypeResolvers.useArgType( 1 ),
StandardFunctionArgumentTypeResolvers.ARGUMENT_OR_IMPLIED_RESULT_TYPE
);
this.numericRenderingSupport = new TruncRenderingSupport(
new PatternRenderer( truncPattern ),
twoArgTruncPattern != null ? new PatternRenderer( twoArgTruncPattern ) : null
);
this.datetimeTrunc = datetimeTrunc;
TruncRenderingSupport renderingSupport = null;
DateTruncEmulation emulation = null;
if ( datetimeTrunc != null ) {
if ( datetimeTrunc.getPattern() != null ) {
renderingSupport = new TruncRenderingSupport( new PatternRenderer( datetimeTrunc.getPattern() ), null );
}
else {
emulation = new DateTruncEmulation( toDateFunction, typeConfiguration );
}
}
this.datetimeRenderingSupport = renderingSupport;
this.dateTruncEmulation = emulation;
}
@Override
protected <T> SelfRenderingSqmFunction<T> generateSqmFunctionExpression(
List<? extends SqmTypedNode<?>> arguments,
ReturnableType<T> impliedResultType,
QueryEngine queryEngine,
TypeConfiguration typeConfiguration) {
final NodeBuilder nodeBuilder = queryEngine.getCriteriaBuilder();
final List<SqmTypedNode<?>> args = new ArrayList<>( arguments );
final FunctionRenderingSupport renderingSupport;
final ArgumentsValidator argumentsValidator;
if ( arguments.size() == 2 && arguments.get( 1 ) instanceof SqmExtractUnit ) {
// datetime truncation
renderingSupport = datetimeRenderingSupport;
argumentsValidator = TruncArgumentsValidator.DATETIME_VALIDATOR;
if ( datetimeTrunc == null ) {
throw new UnsupportedOperationException( "Datetime truncation is not supported for this database" );
}
if ( datetimeTrunc.getPattern() == null ) {
return dateTruncEmulation.generateSqmFunctionExpression(
arguments,
impliedResultType,
queryEngine,
typeConfiguration
);
}
else if ( datetimeTrunc == DatetimeTrunc.TRUNC ) {
// the trunc() function requires translating the temporal_unit to a format string
final TemporalUnit temporalUnit = ( (SqmExtractUnit<?>) arguments.get( 1 ) ).getUnit();
final String pattern;
switch ( temporalUnit ) {
case YEAR:
pattern = "YYYY";
break;
case MONTH:
pattern = "MM";
break;
case WEEK:
pattern = "IW";
break;
case DAY:
pattern = "DD";
break;
case HOUR:
pattern = "HH";
break;
case MINUTE:
pattern = "MI";
break;
case SECOND:
pattern = "SS";
break;
default:
throw new UnsupportedOperationException( "Temporal unit not supported [" + temporalUnit + "]" );
}
// replace temporal_unit parameter with translated string format literal
args.set( 1, new SqmLiteral<>(
pattern,
typeConfiguration.getBasicTypeForJavaType( String.class ),
nodeBuilder
) );
}
}
else {
// numeric truncation
renderingSupport = numericRenderingSupport;
argumentsValidator = TruncArgumentsValidator.NUMERIC_VALIDATOR;
}
return new SelfRenderingSqmFunction<>(
this,
renderingSupport,
args,
impliedResultType,
argumentsValidator,
getReturnTypeResolver(),
queryEngine.getCriteriaBuilder(),
getName()
);
}
private static class TruncRenderingSupport implements FunctionRenderingSupport {
private final PatternRenderer truncPattern;
private final PatternRenderer twoArgTruncPattern;
public TruncRenderingSupport(PatternRenderer truncPattern, PatternRenderer twoArgTruncPattern) {
this.truncPattern = truncPattern;
this.twoArgTruncPattern = twoArgTruncPattern;
}
@Override
public void render(
SqlAppender sqlAppender,
List<? extends SqlAstNode> sqlAstArguments,
SqlAstTranslator<?> walker) {
final PatternRenderer pattern;
if ( twoArgTruncPattern != null && sqlAstArguments.size() == 2 ) {
pattern = twoArgTruncPattern;
}
else {
pattern = truncPattern;
}
pattern.render( sqlAppender, sqlAstArguments, walker );
}
}
protected static class TruncArgumentsValidator implements ArgumentsValidator {
protected static final ArgumentTypesValidator DATETIME_VALIDATOR = new ArgumentTypesValidator(
StandardArgumentsValidators.exactly( 2 ),
TEMPORAL,
TEMPORAL_UNIT
);
protected static final ArgumentTypesValidator NUMERIC_VALIDATOR = new ArgumentTypesValidator(
StandardArgumentsValidators.between( 1, 2 ),
NUMERIC,
NUMERIC
);
@Override
public void validate(
List<? extends SqmTypedNode<?>> arguments,
String functionName,
TypeConfiguration typeConfiguration) {
if ( arguments.size() == 2 && arguments.get( 1 ) instanceof SqmExtractUnit ) {
DATETIME_VALIDATOR.validate( arguments, functionName, typeConfiguration );
}
else {
NUMERIC_VALIDATOR.validate( arguments, functionName, typeConfiguration );
}
}
}
}

View File

@ -4327,6 +4327,25 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
);
}
@Override
public Object visitTruncFunction(HqlParser.TruncFunctionContext ctx) {
final SqmExpression<?> expression = (SqmExpression<?>) ctx.getChild( 2 ).accept( this );
final SqmTypedNode<?> secondArg;
if ( ctx.getChildCount() == 6 ) {
secondArg = (SqmTypedNode<?>) ctx.getChild( 4 ).accept( this );
}
else {
secondArg = null;
}
return getFunctionDescriptor( "trunc" ).generateSqmExpression(
secondArg == null ? singletonList( expression ) : asList( expression, secondArg ),
null,
creationContext.getQueryEngine(),
creationContext.getJpaMetamodel().getTypeConfiguration()
);
}
@Override
public Object visitFormat(HqlParser.FormatContext ctx) {
final String format = QuotingHelper.unquoteStringLiteral( ctx.getChild( 0 ).getText() );

View File

@ -16,7 +16,6 @@ import java.util.Date;
import java.util.List;
import org.hibernate.dialect.CockroachDialect;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
@ -24,6 +23,7 @@ import org.hibernate.testing.orm.domain.StandardDomainModel;
import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.SessionFactory;
@ -435,4 +435,32 @@ public class CriteriaBuilderNonStandardFunctionsTest {
assertEquals( Math.PI, result.get( 1, Double.class ), 1e-9 );
} );
}
@Test
@JiraKey("HHH-16185")
public void testCustomFunctionTrunc(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final CriteriaBuilder cb = session.getCriteriaBuilder();
final CriteriaQuery<Tuple> query = cb.createTupleQuery();
query.multiselect(
cb.function( "trunc", Float.class, cb.literal( 32.92345f ) ),
cb.function( "truncate", Float.class, cb.literal( 32.92345f ) ),
cb.function( "trunc", Float.class, cb.literal( 32.92345f ), cb.literal( 3 ) ),
cb.function( "truncate", Float.class, cb.literal( 32.92345f ), cb.literal( 3 ) ),
cb.function( "trunc", Double.class, cb.literal( 32.92345d ) ),
cb.function( "truncate", Double.class, cb.literal( 32.92345d ) ),
cb.function( "trunc", Double.class, cb.literal( 32.92345d ), cb.literal( 3 ) ),
cb.function( "truncate", Double.class, cb.literal( 32.92345d ), cb.literal( 3 ) )
);
final Tuple result = session.createQuery( query ).getSingleResult();
assertEquals( 32f, result.get( 0 ) );
assertEquals( 32f, result.get( 1 ) );
assertEquals( 32.923f, result.get( 2 ) );
assertEquals( 32.923f, result.get( 3 ) );
assertEquals( 32d, result.get( 4 ) );
assertEquals( 32d, result.get( 5 ) );
assertEquals( 32.923d, result.get( 6 ) );
assertEquals( 32.923d, result.get( 7 ) );
} );
}
}

View File

@ -517,12 +517,12 @@ public class FunctionTests {
public void testDateTruncFunction(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "select date_trunc(year,current_timestamp)", Timestamp.class ).getSingleResult();
session.createQuery( "select date_trunc(month,current_timestamp)", Timestamp.class ).getSingleResult();
session.createQuery( "select date_trunc(day,current_timestamp)", Timestamp.class ).getSingleResult();
session.createQuery( "select date_trunc(hour,current_timestamp)", Timestamp.class ).getSingleResult();
session.createQuery( "select date_trunc(minute,current_timestamp)", Timestamp.class ).getSingleResult();
session.createQuery( "select date_trunc(second,current_timestamp)", Timestamp.class ).getSingleResult();
session.createQuery( "select trunc(current_timestamp,year)", Timestamp.class ).getSingleResult();
session.createQuery( "select trunc(current_timestamp,month)", Timestamp.class ).getSingleResult();
session.createQuery( "select trunc(current_timestamp,day)", Timestamp.class ).getSingleResult();
session.createQuery( "select truncate(current_timestamp,hour)", Timestamp.class ).getSingleResult();
session.createQuery( "select truncate(current_timestamp,minute)", Timestamp.class ).getSingleResult();
session.createQuery( "select truncate(current_timestamp,second)", Timestamp.class ).getSingleResult();
}
);
}