HHH-16185 Custom trunc/truncate implementation that handles both numeric and datetimes
This commit is contained in:
parent
821e31b481
commit
546d145d88
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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" );
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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" );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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" );
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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" );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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( ')' );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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() );
|
||||
|
|
|
@ -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 ) );
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue