HHH-13785 : HQL/Criteria function support

- temporal literals
- generalized literals
- literal formatters (SQL string generation)
- FORMAT function
This commit is contained in:
Steve Ebersole 2020-01-06 08:38:08 -06:00
parent 6e0d15b134
commit eab6107ec2
65 changed files with 2068 additions and 359 deletions

View File

@ -0,0 +1,33 @@
= Query
== Expressions
=== Paths
=== Literals
=== Parameters
=== Unary expressions
=== Arithmetic operations
Numeric v. temporal
=== Functions
- Standard functions
- SqmFunctionRegistry
- Role of Dialect
=== Concatenation operations
=== Entity-type references
=== CASE statements
=== COALESCE statements
=== NULLIF statements
=== Sub-queries

10
design/doc-temporal.adoc Normal file
View File

@ -0,0 +1,10 @@
`OffsetDateTime` is not safe to store in database. This form does not understand "zone rules" relating to things
such as DST. An offset of +5, e.g., does not change when DST starts/ends - its just +5.
A `ZonedDateTime` on the other hand knows the actual timezone as well as the offset for the LocalDateTime portion in
that timezone. It is much more complete picture of the actual Instant.
The proper solution for storing "with tz" would be to always use a `ZonedDateTime`, converted from `OffsetDateTime`
if needed. In this case, I assume we need to transform a `LocalDateTime` to `ZonedDateTime`?
^^ what about Dialects that do not support "with tz" datatype variants? Are there any anymore?

View File

@ -13,7 +13,7 @@ ext {
junitVintageVersion = '5.3.1' junitVintageVersion = '5.3.1'
junit5Version = '5.3.1' junit5Version = '5.3.1'
h2Version = '1.4.196' h2Version = '1.4.199'
bytemanVersion = '4.0.8' //Compatible with JDK14 bytemanVersion = '4.0.8' //Compatible with JDK14
jnpVersion = '5.0.6.CR1' jnpVersion = '5.0.6.CR1'

View File

@ -11,60 +11,91 @@ lexer grammar HqlLexer;
package org.hibernate.grammars.hql; package org.hibernate.grammars.hql;
} }
WS : ( ' ' | '\t' | '\f' | EOL ) -> skip; WS : WS_CHAR+ -> skip;
fragment fragment
EOL : [\r\n]+; WS_CHAR : [ \f\t\r\n];
INTEGER_LITERAL : INTEGER_NUMBER ;
fragment fragment
INTEGER_NUMBER : ('0' | '1'..'9' '0'..'9'*) ; DIGIT : [0-9];
LONG_LITERAL : INTEGER_NUMBER ('l'|'L');
BIG_INTEGER_LITERAL : INTEGER_NUMBER ('bi'|'BI') ;
HEX_LITERAL : '0' ('x'|'X') HEX_DIGIT+ ('l'|'L')? ;
fragment fragment
HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ; HEX_DIGIT : [0-9a-fA-F];
OCTAL_LITERAL : '0' ('0'..'7')+ ('l'|'L')? ; fragment
EXPONENT : [eE] [+-]? DIGIT+;
FLOAT_LITERAL : FLOATING_POINT_NUMBER ('f'|'F')? ; fragment
LONG_SUFFIX : [lL];
fragment
FLOAT_SUFFIX : [fF];
fragment
DOUBLE_SUFFIX : [dD];
fragment
BIG_DECIMAL_SUFFIX : [bB] [dD];
fragment
BIG_INTEGER_SUFFIX : [bB] [iI];
fragment
INTEGER_NUMBER
: DIGIT+
;
fragment fragment
FLOATING_POINT_NUMBER FLOATING_POINT_NUMBER
: ('0'..'9')+ '.' ('0'..'9')* EXPONENT? : DIGIT+ '.' DIGIT* EXPONENT?
| '.' ('0'..'9')+ EXPONENT? | '.' DIGIT+ EXPONENT?
| ('0'..'9')+ EXPONENT | DIGIT+ EXPONENT
| ('0'..'9')+ | DIGIT+
; ;
DOUBLE_LITERAL : FLOATING_POINT_NUMBER ('d'|'D') ;
BIG_DECIMAL_LITERAL : FLOATING_POINT_NUMBER ('bd'|'BD') ;
fragment fragment SINGLE_QUOTE : '\'';
EXPONENT : ('e'|'E') ('+'|'-')? ('0'..'9')+ ; fragment DOUBLE_QUOTE : '"';
CHARACTER_LITERAL fragment BACKSLASH : '\\';
: '\'' ( ESCAPE_SEQUENCE | ~('\''|'\\') ) '\'' {setText(getText().substring(1, getText().length()-1));}
;
STRING_LITERAL
: '"' ( ESCAPE_SEQUENCE | ~('\\'|'"') )* '"' {setText(getText().substring(1, getText().length()-1));}
| ('\'' ( ESCAPE_SEQUENCE | ~('\\'|'\'') )* '\'')+ {setText(getText().substring(1, getText().length()-1).replace("''", "'"));}
;
fragment fragment
ESCAPE_SEQUENCE ESCAPE_SEQUENCE
: '\\' ('b'|'t'|'n'|'f'|'r'|'\\"'|'\''|'\\') : BACKSLASH [btnfr"']
| UNICODE_ESCAPE | BACKSLASH UNICODE_ESCAPE
| OCTAL_ESCAPE | BACKSLASH BACKSLASH
; ;
fragment
UNICODE_ESCAPE
: 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
;
INTEGER_LITERAL : INTEGER_NUMBER ;
LONG_LITERAL : INTEGER_NUMBER LONG_SUFFIX;
FLOAT_LITERAL : FLOATING_POINT_NUMBER FLOAT_SUFFIX?;
DOUBLE_LITERAL : FLOATING_POINT_NUMBER DOUBLE_SUFFIX;
BIG_INTEGER_LITERAL : INTEGER_NUMBER BIG_INTEGER_SUFFIX;
BIG_DECIMAL_LITERAL : FLOATING_POINT_NUMBER BIG_DECIMAL_SUFFIX;
HEX_LITERAL : '0' [xX] HEX_DIGIT+ LONG_SUFFIX?;
OCTAL_LITERAL : '0' ('0'..'7')+ ('l'|'L')? ;
STRING_LITERAL
: DOUBLE_QUOTE ( ~[\\"] | ESCAPE_SEQUENCE | DOUBLE_QUOTE DOUBLE_QUOTE )* DOUBLE_QUOTE
{ setText(getText().substring(1, getText().length()-1).replace("\"\"", "\"")); }
| SINGLE_QUOTE ( ~[\\'] | ESCAPE_SEQUENCE | SINGLE_QUOTE SINGLE_QUOTE )* SINGLE_QUOTE
{ setText(getText().substring(1, getText().length()-1).replace("''", "'")); }
;
fragment fragment
OCTAL_ESCAPE OCTAL_ESCAPE
: '\\' ('0'..'3') ('0'..'7') ('0'..'7') : '\\' ('0'..'3') ('0'..'7') ('0'..'7')
@ -72,10 +103,6 @@ OCTAL_ESCAPE
| '\\' ('0'..'7') | '\\' ('0'..'7')
; ;
fragment
UNICODE_ESCAPE
: '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
;
// ESCAPE start tokens // ESCAPE start tokens
TIMESTAMP_ESCAPE_START : '{ts'; TIMESTAMP_ESCAPE_START : '{ts';
@ -134,6 +161,7 @@ CONCAT : [cC] [oO] [nN] [cC] [aA] [tT];
COUNT : [cC] [oO] [uU] [nN] [tT]; COUNT : [cC] [oO] [uU] [nN] [tT];
CROSS : [cC] [rR] [oO] [sS] [sS]; CROSS : [cC] [rR] [oO] [sS] [sS];
CURRENT_DATE : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [dD] [aA] [tT] [eE]; CURRENT_DATE : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [dD] [aA] [tT] [eE];
CURRENT_DATETIME : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [dD] [aA] [tT] [eE] [tT] [iI] [mM] [eE];
CURRENT_INSTANT : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [iI] [nN] [sS] [tT] [aA] [nN] [tT]; CURRENT_INSTANT : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [iI] [nN] [sS] [tT] [aA] [nN] [tT];
CURRENT_TIME : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [tT] [iI] [mM] [eE]; CURRENT_TIME : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [tT] [iI] [mM] [eE];
CURRENT_TIMESTAMP : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [tT] [iI] [mM] [eE] [sS] [tT] [aA] [mM] [pP]; CURRENT_TIMESTAMP : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [tT] [iI] [mM] [eE] [sS] [tT] [aA] [mM] [pP];
@ -155,6 +183,7 @@ FETCH : [fF] [eE] [tT] [cC] [hH];
FLOOR : [fF] [lL] [oO] [oO] [rR]; FLOOR : [fF] [lL] [oO] [oO] [rR];
FROM : [fF] [rR] [oO] [mM]; FROM : [fF] [rR] [oO] [mM];
FOR : [fF] [oO] [rR]; FOR : [fF] [oO] [rR];
FORMAT : [fF] [oO] [rR] [mM] [aA] [tT];
FULL : [fF] [uU] [lL] [lL]; FULL : [fF] [uU] [lL] [lL];
FUNCTION : [fF] [uU] [nN] [cC] [tT] [iI] [oO] [nN]; FUNCTION : [fF] [uU] [nN] [cC] [tT] [iI] [oO] [nN];
GREATEST : [gG] [rR] [eE] [aA] [tT] [eE] [sS] [tT]; GREATEST : [gG] [rR] [eE] [aA] [tT] [eE] [sS] [tT];

View File

@ -466,7 +466,6 @@ nullIf
literal literal
: STRING_LITERAL : STRING_LITERAL
| CHARACTER_LITERAL
| INTEGER_LITERAL | INTEGER_LITERAL
| LONG_LITERAL | LONG_LITERAL
| BIG_INTEGER_LITERAL | BIG_INTEGER_LITERAL
@ -478,40 +477,79 @@ literal
| NULL | NULL
| TRUE | TRUE
| FALSE | FALSE
| timestampLiteral | temporalLiteral
| dateLiteral | generalizedLiteral
| timeLiteral
; ;
// todo (6.0) : expand temporal literal support to Java 8 temporal types temporalLiteral
// * Instant -> {instant '...'} : dateTimeLiteral
// * LocalDate -> {localDate '...'} | dateLiteral
// * LocalDateTime -> {localDateTime '...'} | timeLiteral
// * OffsetDateTime -> {offsetDateTime '...'} | jdbcTimestampLiteral
// * OffsetTime -> {offsetTime '...'} | jdbcDateLiteral
// * ZonedDateTime -> {localDate '...'} | jdbcTimeLiteral
// * ... ;
//
// Few things:
// 1) the markers above are just initial thoughts. They are obviously verbose. Maybe acronyms or shortened forms would be better
// 2) we may want to stay away from all of the timezone headaches by not supporting local, zoned and offset forms
timestampLiteral dateTimeLiteral
: TIMESTAMP_ESCAPE_START dateTimeLiteralText RIGHT_BRACE : LEFT_BRACE dateTime RIGHT_BRACE
; ;
dateLiteral dateLiteral
: DATE_ESCAPE_START dateTimeLiteralText RIGHT_BRACE : LEFT_BRACE date RIGHT_BRACE
; ;
timeLiteral timeLiteral
: TIME_ESCAPE_START dateTimeLiteralText RIGHT_BRACE : LEFT_BRACE time RIGHT_BRACE
; ;
dateTimeLiteralText dateTime
: STRING_LITERAL | CHARACTER_LITERAL : date time (zoneId | offset)?
; ;
date
: year MINUS month MINUS day
;
time
: hour COLON minute (COLON second)?
;
offset
: (PLUS | MINUS) hour (COLON minute)?
;
year: INTEGER_LITERAL;
month: INTEGER_LITERAL;
day: INTEGER_LITERAL;
hour: INTEGER_LITERAL;
minute: INTEGER_LITERAL;
second: INTEGER_LITERAL | FLOAT_LITERAL;
zoneId: STRING_LITERAL;
jdbcTimestampLiteral
: TIMESTAMP_ESCAPE_START (dateTime | genericTemporalLiteralText) RIGHT_BRACE
;
jdbcDateLiteral
: DATE_ESCAPE_START (date | genericTemporalLiteralText) RIGHT_BRACE
;
jdbcTimeLiteral
: TIME_ESCAPE_START (time | genericTemporalLiteralText) RIGHT_BRACE
;
genericTemporalLiteralText
: STRING_LITERAL
;
generalizedLiteral
: LEFT_BRACE generalizedLiteralType COLON generalizedLiteralText RIGHT_BRACE
;
generalizedLiteralType : STRING_LITERAL;
generalizedLiteralText : STRING_LITERAL;
parameter parameter
: COLON identifier # NamedParameter : COLON identifier # NamedParameter
| QUESTION_MARK INTEGER_LITERAL? # PositionalParameter | QUESTION_MARK INTEGER_LITERAL? # PositionalParameter
@ -589,6 +627,7 @@ countFunction
standardFunction standardFunction
: castFunction : castFunction
| extractFunction | extractFunction
| formatFunction
| concatFunction | concatFunction
| substringFunction | substringFunction
| replaceFunction | replaceFunction
@ -666,7 +705,7 @@ trimSpecification
; ;
trimCharacter trimCharacter
: CHARACTER_LITERAL | STRING_LITERAL : STRING_LITERAL
; ;
upperFunction upperFunction

View File

@ -19,7 +19,6 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;

View File

@ -15,6 +15,7 @@ import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.H2ExtractEmulation; import org.hibernate.dialect.function.H2ExtractEmulation;
import org.hibernate.dialect.function.Replacer;
import org.hibernate.dialect.hint.IndexQueryHintHandler; import org.hibernate.dialect.hint.IndexQueryHintHandler;
import org.hibernate.dialect.identity.H2IdentityColumnSupport; import org.hibernate.dialect.identity.H2IdentityColumnSupport;
import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport;
@ -205,6 +206,19 @@ public class H2Dialect extends Dialect {
sqlAppender.append(")"); sqlAppender.append(")");
} }
public String translateExtractField(TemporalUnit unit) {
switch ( unit ) {
case DAY_OF_MONTH: return "day";
case WEEK: return "iso_week";
default: return unit.toString();
}
}
@Override
public String translateDatetimeFormat(String format) {
return new Replacer( format, "'", "''" ).replace( "e", "u").result(); //NICE!!
}
@Override @Override
public String getAddColumnString() { public String getAddColumnString() {
return "add column"; return "add column";

View File

@ -13,6 +13,7 @@ import java.util.Locale;
import org.hibernate.boot.TempTableDdlTransactionHandling; import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.InformixExtractEmulation; import org.hibernate.dialect.function.InformixExtractEmulation;
import org.hibernate.dialect.function.Replacer;
import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.InformixIdentityColumnSupport; import org.hibernate.dialect.identity.InformixIdentityColumnSupport;
import org.hibernate.dialect.pagination.FirstLimitHandler; import org.hibernate.dialect.pagination.FirstLimitHandler;
@ -117,6 +118,65 @@ public class InformixDialect extends Dialect {
} }
@Override
public String translateDatetimeFormat(String format) {
//Informix' own variation of MySQL
return datetimeFormat( format ).result();
}
public static Replacer datetimeFormat(String format) {
return new Replacer( format, "'", "" )
.replace("%", "%%")
//year
.replace("yyyy", "%Y")
.replace("yyy", "%Y")
.replace("yy", "%y")
.replace("y", "Y")
//month of year
.replace("MMMM", "%B")
.replace("MMM", "%b")
.replace("MM", "%m")
.replace("M", "%c") //????
//day of week
.replace("EEEE", "%A")
.replace("EEE", "%a")
.replace("ee", "%w")
.replace("e", "%w")
//day of month
.replace("dd", "%d")
.replace("d", "%e")
//am pm
.replace("aa", "%p") //?????
.replace("a", "%p") //?????
//hour
.replace("hh", "%I")
.replace("HH", "%H")
.replace("h", "%I")
.replace("H", "%H")
//minute
.replace("mm", "%M")
.replace("m", "%M")
//second
.replace("ss", "%S")
.replace("s", "%S")
//fractional seconds
.replace("SSSSSS", "%F50") //5 is the max
.replace("SSSSS", "%F5")
.replace("SSSS", "%F4")
.replace("SSS", "%F3")
.replace("SS", "%F2")
.replace("S", "%F1");
}
@Override @Override
public String getAddColumnString() { public String getAddColumnString() {
return "add"; return "add";

View File

@ -188,6 +188,22 @@ public class IngresDialect extends Dialect {
sqlAppender.append(")"); sqlAppender.append(")");
} }
@Override
public String translateDatetimeFormat(String format) {
return MySQLDialect.datetimeFormat( format ).result();
}
@Override
public String translateExtractField(TemporalUnit unit) {
switch ( unit ) {
case DAY_OF_MONTH: return "day";
case DAY_OF_YEAR: return "doy";
case DAY_OF_WEEK: return "dow";
case WEEK: return "iso_week";
default: return unit.toString();
}
}
@Override @Override
public String getSelectGUIDString() { public String getSelectGUIDString() {
return "select uuid_to_char(uuid_create())"; return "select uuid_to_char(uuid_create())";

View File

@ -20,6 +20,7 @@ import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.MySQLExtractEmulation; import org.hibernate.dialect.function.MySQLExtractEmulation;
import org.hibernate.dialect.function.Replacer;
import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.MySQLIdentityColumnSupport; import org.hibernate.dialect.identity.MySQLIdentityColumnSupport;
import org.hibernate.dialect.pagination.AbstractLimitHandler; import org.hibernate.dialect.pagination.AbstractLimitHandler;
@ -242,6 +243,80 @@ public class MySQLDialect extends Dialect {
} }
} }
@Override
public String translateDatetimeFormat(String format) {
return datetimeFormat( format ).result();
}
public static Replacer datetimeFormat(String format) {
return new Replacer( format, "'", "" )
.replace("%", "%%")
//year
.replace("yyyy", "%Y")
.replace("yyy", "%Y")
.replace("yy", "%y")
.replace("y", "%Y")
//month of year
.replace("MMMM", "%M")
.replace("MMM", "%b")
.replace("MM", "%m")
.replace("M", "%c")
//week of year
.replace("ww", "%v")
.replace("w", "%v")
//year for week
.replace("YYYY", "%x")
.replace("YYY", "%x")
.replace("Y", "%x")
//week of month
//????
//day of week
.replace("EEEE", "%W")
.replace("EEE", "%a")
.replace("ee", "%w")
.replace("e", "%w")
//day of month
.replace("dd", "%d")
.replace("d", "%e")
//day of year
.replace("DDD", "%j")
.replace("DD", "%j")
.replace("D", "%j")
//am pm
.replace("aa", "%p")
.replace("a", "%p")
//hour
.replace("hh", "%h")
.replace("HH", "%H")
.replace("h", "%l")
.replace("H", "%k")
//minute
.replace("mm", "%i")
.replace("m", "%i")
//second
.replace("ss", "%S")
.replace("s", "%S")
//fractional seconds
.replace("SSSSSS", "%f")
.replace("SSSSS", "%f")
.replace("SSSS", "%f")
.replace("SSS", "%f")
.replace("SS", "%f")
.replace("S", "%f");
}
void upgradeTo57() { void upgradeTo57() {
// For details about MySQL 5.7 support for fractional seconds // For details about MySQL 5.7 support for fractional seconds

View File

@ -21,6 +21,7 @@ import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.NvlCoalesceEmulation; import org.hibernate.dialect.function.NvlCoalesceEmulation;
import org.hibernate.dialect.function.OracleExtractEmulation; import org.hibernate.dialect.function.OracleExtractEmulation;
import org.hibernate.dialect.function.Replacer;
import org.hibernate.dialect.pagination.AbstractLimitHandler; import org.hibernate.dialect.pagination.AbstractLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.LimitHelper; import org.hibernate.dialect.pagination.LimitHelper;

View File

@ -22,6 +22,7 @@ import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.PostgresExtractEmulation; import org.hibernate.dialect.function.PostgresExtractEmulation;
import org.hibernate.dialect.function.Replacer;
import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.PostgreSQL81IdentityColumnSupport; import org.hibernate.dialect.identity.PostgreSQL81IdentityColumnSupport;
import org.hibernate.dialect.pagination.AbstractLimitHandler; import org.hibernate.dialect.pagination.AbstractLimitHandler;

View File

@ -12,12 +12,14 @@ import java.util.Locale;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.Replacer;
import org.hibernate.dialect.function.TransactSQLTrimEmulation; import org.hibernate.dialect.function.TransactSQLTrimEmulation;
import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.SQLServerIdentityColumnSupport; import org.hibernate.dialect.identity.SQLServerIdentityColumnSupport;
import org.hibernate.dialect.pagination.LegacyLimitHandler; import org.hibernate.dialect.pagination.LegacyLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.TopLimitHandler; import org.hibernate.dialect.pagination.TopLimitHandler;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryEngine;
import org.hibernate.type.descriptor.sql.SmallIntTypeDescriptor; import org.hibernate.type.descriptor.sql.SmallIntTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
@ -59,6 +61,58 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
queryEngine.getSqmFunctionRegistry().register( "trim", new TransactSQLTrimEmulation() ); queryEngine.getSqmFunctionRegistry().register( "trim", new TransactSQLTrimEmulation() );
} }
@Override
public String translateExtractField(TemporalUnit unit) {
switch ( unit ) {
case WEEK: return "isowk"; //the ISO week number (behavior of "week" depends on a system property)
default: return super.translateExtractField(unit);
}
}
@Override
public String translateDatetimeFormat(String format) {
return datetimeFormat(format).result();
}
public static Replacer datetimeFormat(String format) {
return new Replacer( format, "'", "\"" )
//era
.replace("G", "g")
//y nothing to do
//M nothing to do
//w no equivalent
//W no equivalent
//Y no equivalent
//day of week
.replace("EEEE", "dddd")
.replace("EEE", "ddd")
//e no equivalent
//d nothing to do
//D no equivalent
//am pm
.replace("aa", "tt")
.replace("a", "tt")
//h nothing to do
//H nothing to do
//m nothing to do
//s nothing to do
//fractional seconds
.replace("S", "F")
//timezones
.replace("XXX", "K") //UTC represented as "Z"
.replace("xxx", "zzz")
.replace("x", "zz");
}
@Override @Override
public String getNoColumnsInsertString() { public String getNoColumnsInsertString() {
return "default values"; return "default values";

View File

@ -7,7 +7,6 @@
package org.hibernate.dialect.function; package org.hibernate.dialect.function;
import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.function.StandardFunctionRenderingSupport;
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators; import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers; import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.StandardBasicTypes;

View File

@ -4,21 +4,24 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later * 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 * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
package org.hibernate.dialect; package org.hibernate.dialect.function;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.hibernate.Internal;
/** /**
* @author Gavin King * @author Gavin King
*/ */
@Internal
public class Replacer { public class Replacer {
private String[] chunks; private String[] chunks;
private String quote; private String quote;
private String delimiter; private String delimiter;
private List<Replacement> replacements = new ArrayList<>(); private List<Replacement> replacements = new ArrayList<>();
class Replacement { public static class Replacement {
String placeholder; String placeholder;
String replacement; String replacement;
@ -45,7 +48,7 @@ public class Replacer {
} }
} }
Replacer(String format, String quote, String delimiter) { public Replacer(String format, String quote, String delimiter) {
this.delimiter = delimiter; this.delimiter = delimiter;
this.chunks = format.split( quote ); this.chunks = format.split( quote );
this.quote = quote; this.quote = quote;

View File

@ -157,7 +157,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
private final boolean[] isNullableTable; private final boolean[] isNullableTable;
private final boolean[] isInverseTable; private final boolean[] isInverseTable;
private final Map<String, String> discriminatorValuesByTableName; private final Map<String, Object> discriminatorValuesByTableName;
private final Map<String, String> subclassNameByTableName; private final Map<String, String> subclassNameByTableName;
//INITIALIZATION: //INITIALIZATION:
@ -532,7 +532,22 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
discriminatorValuesByTableName = new LinkedHashMap<>( subclassSpan + 1 ); discriminatorValuesByTableName = new LinkedHashMap<>( subclassSpan + 1 );
subclassNameByTableName = new HashMap<>( subclassSpan + 1); subclassNameByTableName = new HashMap<>( subclassSpan + 1);
discriminatorValuesByTableName.put( persistentClass.getTable().getName(), discriminatorSQLString); // We need to convert the `discriminatorSQLString` (which is a String read from boot-mapping) into
// the type indicated by `#discriminatorType` (String -> Integer, e.g.).
try {
final Object convertedDiscriminatorValue = discriminatorType.stringToObject( discriminatorSQLString );
discriminatorValuesByTableName.put( persistentClass.getTable().getName(), convertedDiscriminatorValue );
}
catch (HibernateException e) {
throw e;
}
catch (Exception e) {
throw new MappingException(
"Could not resolve specified discriminator value [" + discriminatorSQLString
+ "] to discriminator type [" + discriminatorType + "]"
);
}
discriminatorValues = new String[subclassSpan]; discriminatorValues = new String[subclassSpan];
discriminatorValues[subclassSpan - 1] = discriminatorSQLString; discriminatorValues[subclassSpan - 1] = discriminatorSQLString;
notNullColumnTableNumbers = new int[subclassSpan]; notNullColumnTableNumbers = new int[subclassSpan];
@ -590,7 +605,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
// "foo.class = Bar" works in HQL // "foo.class = Bar" works in HQL
discriminatorValue = sc.getSubclassId(); discriminatorValue = sc.getSubclassId();
} }
discriminatorValuesByTableName.put( sc.getTable().getName(), discriminatorValue.toString() ); discriminatorValuesByTableName.put( sc.getTable().getName(), discriminatorValue );
subclassesByDiscriminatorValue.put( discriminatorValue, sc.getEntityName() ); subclassesByDiscriminatorValue.put( discriminatorValue, sc.getEntityName() );
discriminatorValues[k] = discriminatorValue.toString(); discriminatorValues[k] = discriminatorValue.toString();
int id = getTableId( int id = getTableId(
@ -1320,8 +1335,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
ColumnReference identifierColumnReference, ColumnReference identifierColumnReference,
BasicType resultType) { BasicType resultType) {
final Predicate predicate = new NullnessPredicate( identifierColumnReference, true ); final Predicate predicate = new NullnessPredicate( identifierColumnReference, true );
final Expression expression = final Expression expression = new QueryLiteral<>(
new QueryLiteral<>(
discriminatorValuesByTableName.get( table.getTableExpression() ), discriminatorValuesByTableName.get( table.getTableExpression() ),
resultType resultType
); );

View File

@ -103,7 +103,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
private final int[] subclassFormulaTableNumberClosure; private final int[] subclassFormulaTableNumberClosure;
// discriminator column // discriminator column
private final Map<Object, String> subclassesByDiscriminatorValue = new HashMap<Object, String>(); private final Map<Object, String> subclassesByDiscriminatorValue = new HashMap<>();
private final boolean forceDiscriminator; private final boolean forceDiscriminator;
private final String discriminatorColumnName; private final String discriminatorColumnName;
private final String discriminatorColumnReaders; private final String discriminatorColumnReaders;

View File

@ -36,6 +36,9 @@ public enum QueryLiteralRendering {
/** /**
* As a parameter when the literal occurs outside the SELECT clause, * As a parameter when the literal occurs outside the SELECT clause,
* otherwise render as a SQL literal. * otherwise render as a SQL literal.
*
* Technically this should be called something like AS_PARAM_UNLESS_SELECTED. If the literal is used as an argument
* to a function call in the select-clause, for example, we'd still want to render as a parameter.
*/ */
AS_PARAM_OUTSIDE_SELECT( "param-outside-select" ); AS_PARAM_OUTSIDE_SELECT( "param-outside-select" );

View File

@ -12,13 +12,19 @@ import java.sql.Date;
import java.sql.Time; import java.sql.Time;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.TemporalAccessor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.TimeZone;
import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.NullPrecedence; import org.hibernate.NullPrecedence;
@ -30,6 +36,7 @@ import org.hibernate.grammars.hql.HqlParserBaseVisitor;
import org.hibernate.internal.util.collections.Stack; import org.hibernate.internal.util.collections.Stack;
import org.hibernate.internal.util.collections.StandardStack; import org.hibernate.internal.util.collections.StandardStack;
import org.hibernate.metamodel.CollectionClassification; import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.mapping.MappingModelExpressable;
import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType; import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType;
import org.hibernate.metamodel.model.domain.BasicDomainType; import org.hibernate.metamodel.model.domain.BasicDomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType;
@ -67,6 +74,7 @@ import org.hibernate.query.sqm.internal.SqmDmlCreationProcessingState;
import org.hibernate.query.sqm.internal.SqmQuerySpecCreationProcessingStateStandardImpl; import org.hibernate.query.sqm.internal.SqmQuerySpecCreationProcessingStateStandardImpl;
import org.hibernate.query.sqm.spi.ParameterDeclarationContext; import org.hibernate.query.sqm.spi.ParameterDeclarationContext;
import org.hibernate.query.sqm.spi.SqmCreationContext; import org.hibernate.query.sqm.spi.SqmCreationContext;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.query.sqm.tree.SqmTypedNode;
@ -80,7 +88,6 @@ import org.hibernate.query.sqm.tree.domain.SqmMinIndexPath;
import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor; import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath; import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
import org.hibernate.query.sqm.tree.expression.LiteralHelper;
import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic; import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic;
import org.hibernate.query.sqm.tree.expression.SqmCaseSearched; import org.hibernate.query.sqm.tree.expression.SqmCaseSearched;
import org.hibernate.query.sqm.tree.expression.SqmCaseSimple; import org.hibernate.query.sqm.tree.expression.SqmCaseSimple;
@ -89,6 +96,7 @@ import org.hibernate.query.sqm.tree.expression.SqmCollectionSize;
import org.hibernate.query.sqm.tree.expression.SqmDistinct; import org.hibernate.query.sqm.tree.expression.SqmDistinct;
import org.hibernate.query.sqm.tree.expression.SqmExpression; import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.expression.SqmExtractUnit; import org.hibernate.query.sqm.tree.expression.SqmExtractUnit;
import org.hibernate.query.sqm.tree.expression.SqmFormat;
import org.hibernate.query.sqm.tree.expression.SqmFunction; import org.hibernate.query.sqm.tree.expression.SqmFunction;
import org.hibernate.query.sqm.tree.expression.SqmLiteral; import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.query.sqm.tree.expression.SqmLiteralNull; import org.hibernate.query.sqm.tree.expression.SqmLiteralNull;
@ -97,6 +105,7 @@ import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.expression.SqmParameterizedEntityType; import org.hibernate.query.sqm.tree.expression.SqmParameterizedEntityType;
import org.hibernate.query.sqm.tree.expression.SqmPathEntityType; import org.hibernate.query.sqm.tree.expression.SqmPathEntityType;
import org.hibernate.query.sqm.tree.expression.SqmPositionalParameter; import org.hibernate.query.sqm.tree.expression.SqmPositionalParameter;
import org.hibernate.query.sqm.tree.expression.SqmSelfRenderingExpression;
import org.hibernate.query.sqm.tree.expression.SqmStar; import org.hibernate.query.sqm.tree.expression.SqmStar;
import org.hibernate.query.sqm.tree.expression.SqmTrimSpecification; import org.hibernate.query.sqm.tree.expression.SqmTrimSpecification;
import org.hibernate.query.sqm.tree.expression.SqmUnaryOperation; import org.hibernate.query.sqm.tree.expression.SqmUnaryOperation;
@ -136,7 +145,9 @@ import org.hibernate.query.sqm.tree.select.SqmSelection;
import org.hibernate.query.sqm.tree.select.SqmSortSpecification; import org.hibernate.query.sqm.tree.select.SqmSortSpecification;
import org.hibernate.query.sqm.tree.select.SqmSubQuery; import org.hibernate.query.sqm.tree.select.SqmSubQuery;
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.DateTimeUtils;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -150,6 +161,7 @@ import static org.hibernate.query.TemporalUnit.DAY_OF_MONTH;
import static org.hibernate.query.TemporalUnit.DAY_OF_WEEK; import static org.hibernate.query.TemporalUnit.DAY_OF_WEEK;
import static org.hibernate.query.TemporalUnit.DAY_OF_YEAR; import static org.hibernate.query.TemporalUnit.DAY_OF_YEAR;
import static org.hibernate.query.TemporalUnit.OFFSET; import static org.hibernate.query.TemporalUnit.OFFSET;
import static org.hibernate.query.TemporalUnit.SECOND;
import static org.hibernate.query.TemporalUnit.TIME; import static org.hibernate.query.TemporalUnit.TIME;
import static org.hibernate.query.TemporalUnit.TIMEZONE_HOUR; import static org.hibernate.query.TemporalUnit.TIMEZONE_HOUR;
import static org.hibernate.query.TemporalUnit.TIMEZONE_MINUTE; import static org.hibernate.query.TemporalUnit.TIMEZONE_MINUTE;
@ -171,6 +183,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
* Main entry point into analysis of HQL/JPQL parse tree - producing a semantic model of the * Main entry point into analysis of HQL/JPQL parse tree - producing a semantic model of the
* query. * query.
*/ */
@SuppressWarnings("WeakerAccess")
public static SqmStatement buildSemanticModel( public static SqmStatement buildSemanticModel(
HqlParser.StatementContext hqlParseTree, HqlParser.StatementContext hqlParseTree,
SqmCreationOptions creationOptions, SqmCreationOptions creationOptions,
@ -195,6 +208,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
return processingStateStack; return processingStateStack;
} }
@SuppressWarnings("WeakerAccess")
public SemanticQueryBuilder(SqmCreationOptions creationOptions, SqmCreationContext creationContext) { public SemanticQueryBuilder(SqmCreationOptions creationOptions, SqmCreationContext creationContext) {
this.creationOptions = creationOptions; this.creationOptions = creationOptions;
this.creationContext = creationContext; this.creationContext = creationContext;
@ -1408,7 +1422,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
return new SqmFunction( return new SqmFunction(
"mod", "mod",
getFunctionDescriptor( "mod" ), getFunctionDescriptor( "mod" ),
(AllowableFunctionReturnType) dividend.getNodeType(), dividend.getNodeType(),
asList( dividend, divisor ), asList( dividend, divisor ),
creationContext.getNodeBuilder() creationContext.getNodeBuilder()
); );
@ -1513,7 +1527,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
return new SqmFunction( return new SqmFunction(
"local_datetime", "local_datetime",
getFunctionDescriptor( "local_datetime" ), getFunctionDescriptor( "local_datetime" ),
StandardBasicTypes.TIMESTAMP, StandardBasicTypes.LOCAL_DATE_TIME,
creationContext.getNodeBuilder() creationContext.getNodeBuilder()
); );
} }
@ -1524,7 +1538,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
return new SqmFunction( return new SqmFunction(
"local_date", "local_date",
getFunctionDescriptor( "local_date" ), getFunctionDescriptor( "local_date" ),
StandardBasicTypes.TIMESTAMP, StandardBasicTypes.LOCAL_DATE,
creationContext.getNodeBuilder() creationContext.getNodeBuilder()
); );
} }
@ -1535,7 +1549,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
return new SqmFunction( return new SqmFunction(
"local_time", "local_time",
getFunctionDescriptor( "local_time" ), getFunctionDescriptor( "local_time" ),
StandardBasicTypes.TIMESTAMP, StandardBasicTypes.LOCAL_TIME,
creationContext.getNodeBuilder() creationContext.getNodeBuilder()
); );
} }
@ -1568,7 +1582,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
return new SqmFunction( return new SqmFunction(
"least", "least",
getFunctionDescriptor( "least" ), getFunctionDescriptor( "least" ),
(AllowableFunctionReturnType<?>) type, type,
arguments, arguments,
creationContext.getNodeBuilder() creationContext.getNodeBuilder()
); );
@ -1637,22 +1651,23 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
@Override @Override
public SqmLiteral visitLiteralExpression(HqlParser.LiteralExpressionContext ctx) { public SqmLiteral visitLiteralExpression(HqlParser.LiteralExpressionContext ctx) {
if ( ctx.literal().CHARACTER_LITERAL() != null ) { if ( ctx.literal().STRING_LITERAL() != null ) {
return characterLiteral( ctx.literal().CHARACTER_LITERAL().getText() );
}
else if ( ctx.literal().STRING_LITERAL() != null ) {
return stringLiteral( ctx.literal().STRING_LITERAL().getText() ); return stringLiteral( ctx.literal().STRING_LITERAL().getText() );
} }
else if ( ctx.literal().INTEGER_LITERAL() != null ) {
if ( ctx.literal().INTEGER_LITERAL() != null ) {
return integerLiteral( ctx.literal().INTEGER_LITERAL().getText() ); return integerLiteral( ctx.literal().INTEGER_LITERAL().getText() );
} }
else if ( ctx.literal().LONG_LITERAL() != null ) {
if ( ctx.literal().LONG_LITERAL() != null ) {
return longLiteral( ctx.literal().LONG_LITERAL().getText() ); return longLiteral( ctx.literal().LONG_LITERAL().getText() );
} }
else if ( ctx.literal().BIG_INTEGER_LITERAL() != null ) {
if ( ctx.literal().BIG_INTEGER_LITERAL() != null ) {
return bigIntegerLiteral( ctx.literal().BIG_INTEGER_LITERAL().getText() ); return bigIntegerLiteral( ctx.literal().BIG_INTEGER_LITERAL().getText() );
} }
else if ( ctx.literal().HEX_LITERAL() != null ) {
if ( ctx.literal().HEX_LITERAL() != null ) {
final String text = ctx.literal().HEX_LITERAL().getText(); final String text = ctx.literal().HEX_LITERAL().getText();
if ( text.endsWith( "l" ) || text.endsWith( "L" ) ) { if ( text.endsWith( "l" ) || text.endsWith( "L" ) ) {
return longLiteral( text ); return longLiteral( text );
@ -1661,7 +1676,8 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
return integerLiteral( text ); return integerLiteral( text );
} }
} }
else if ( ctx.literal().OCTAL_LITERAL() != null ) {
if ( ctx.literal().OCTAL_LITERAL() != null ) {
final String text = ctx.literal().OCTAL_LITERAL().getText(); final String text = ctx.literal().OCTAL_LITERAL().getText();
if ( text.endsWith( "l" ) || text.endsWith( "L" ) ) { if ( text.endsWith( "l" ) || text.endsWith( "L" ) ) {
return longLiteral( text ); return longLiteral( text );
@ -1670,56 +1686,379 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
return integerLiteral( text ); return integerLiteral( text );
} }
} }
else if ( ctx.literal().FLOAT_LITERAL() != null ) {
if ( ctx.literal().FLOAT_LITERAL() != null ) {
return floatLiteral( ctx.literal().FLOAT_LITERAL().getText() ); return floatLiteral( ctx.literal().FLOAT_LITERAL().getText() );
} }
else if ( ctx.literal().DOUBLE_LITERAL() != null ) {
if ( ctx.literal().DOUBLE_LITERAL() != null ) {
return doubleLiteral( ctx.literal().DOUBLE_LITERAL().getText() ); return doubleLiteral( ctx.literal().DOUBLE_LITERAL().getText() );
} }
else if ( ctx.literal().BIG_DECIMAL_LITERAL() != null ) {
if ( ctx.literal().BIG_DECIMAL_LITERAL() != null ) {
return bigDecimalLiteral( ctx.literal().BIG_DECIMAL_LITERAL().getText() ); return bigDecimalLiteral( ctx.literal().BIG_DECIMAL_LITERAL().getText() );
} }
else if ( ctx.literal().FALSE() != null ) {
if ( ctx.literal().FALSE() != null ) {
return booleanLiteral( false ); return booleanLiteral( false );
} }
else if ( ctx.literal().TRUE() != null ) {
if ( ctx.literal().TRUE() != null ) {
return booleanLiteral( true ); return booleanLiteral( true );
} }
else if ( ctx.literal().NULL() != null ) {
if ( ctx.literal().NULL() != null ) {
return new SqmLiteralNull( creationContext.getQueryEngine().getCriteriaBuilder() ); return new SqmLiteralNull( creationContext.getQueryEngine().getCriteriaBuilder() );
} }
else if ( ctx.literal().timestampLiteral() != null ) {
return LiteralHelper.timestampLiteralFrom( ctx.literal().timestampLiteral().dateTimeLiteralText().getText(), this ); if ( ctx.literal().temporalLiteral() != null ) {
return interpretTemporalLiteral( ctx.literal().temporalLiteral() );
} }
else if ( ctx.literal().dateLiteral() != null ) {
return LiteralHelper.dateLiteralFrom( ctx.literal().dateLiteral().dateTimeLiteralText().getText(), this ); if ( ctx.literal().generalizedLiteral() != null ) {
} throw new NotYetImplementedFor6Exception( getClass() );
else if ( ctx.literal().timeLiteral() != null ) {
return LiteralHelper.timeLiteralFrom( ctx.literal().timeLiteral().dateTimeLiteralText().getText(), this );
} }
// otherwise we have a problem // otherwise we have a problem
throw new ParsingException( "Unexpected literal expression type [" + ctx.getText() + "]" ); throw new ParsingException( "Unexpected literal expression type [" + ctx.getText() + "]" );
} }
private SqmLiteral interpretTemporalLiteral(HqlParser.TemporalLiteralContext temporalLiteral) {
if ( temporalLiteral.dateTimeLiteral() != null ) {
return interpretDateTimeLiteral( temporalLiteral.dateTimeLiteral() );
}
if ( temporalLiteral.dateLiteral() != null ) {
return interpretDateLiteral( temporalLiteral.dateLiteral() );
}
if ( temporalLiteral.timeLiteral() != null ) {
return interpretTimeLiteral( temporalLiteral.timeLiteral() );
}
if ( temporalLiteral.jdbcTimestampLiteral() != null ) {
return interpretJdbcDateTimeLiteral( temporalLiteral.jdbcTimestampLiteral() );
}
if ( temporalLiteral.jdbcDateLiteral() != null ) {
return interpretJdbcDateLiteral( temporalLiteral.jdbcDateLiteral() );
}
if ( temporalLiteral.jdbcTimeLiteral() != null ) {
return interpretJdbcTimeLiteral( temporalLiteral.jdbcTimeLiteral() );
}
// otherwise we have a problem
throw new ParsingException( "Could not interpret temporal literal expression [" + temporalLiteral.getText() + "]" );
}
private SqmLiteral interpretDateTimeLiteral(HqlParser.DateTimeLiteralContext dateTimeLiteral) {
final HqlParser.DateTimeContext dateTimeCtx = dateTimeLiteral.dateTime();
final HqlParser.DateContext dateCtx = dateTimeCtx.date();
final HqlParser.TimeContext timeCtx = dateTimeCtx.time();
final LocalDate localDate = localDate( dateCtx );
final LocalTime localTime = localTime( timeCtx );
if ( dateTimeCtx.zoneId() == null && dateTimeCtx.offset() == null ) {
return new SqmLiteral<>(
LocalDateTime.of( localDate, localTime ),
basicType( LocalDateTime.class ),
creationContext.getNodeBuilder()
);
}
if ( dateTimeCtx.zoneId() != null ) {
// we have a "zone id", which could still identify an offset rather that a ZoneId
try {
//noinspection unchecked
return new SqmLiteral(
ZonedDateTime.of(
localDate,
localTime,
ZoneId.of( dateTimeCtx.zoneId().STRING_LITERAL().getText() )
),
basicType( OffsetDateTime.class ),
creationContext.getNodeBuilder()
);
}
catch (Exception e) {
//noinspection unchecked
return new SqmLiteral(
ZonedDateTime.of(
localDate,
localTime,
TimeZone.getTimeZone( dateTimeCtx.zoneId().STRING_LITERAL().getText() ).toZoneId()
),
basicType( ZonedDateTime.class ),
creationContext.getNodeBuilder()
);
}
}
final HqlParser.OffsetContext offsetCtx = dateTimeCtx.offset();
assert offsetCtx != null;
// handle the offset
final int hourCtx = Integer.parseInt( offsetCtx.hour().INTEGER_LITERAL().getText() );
final int hour = offsetCtx.MINUS() == null
? hourCtx
: 0 - hourCtx;
final ZoneOffset offset = ZoneOffset.ofHoursMinutes(
hour,
offsetCtx.minute() == null
? 0
: Integer.parseInt( offsetCtx.minute().INTEGER_LITERAL().getText() )
);
//noinspection unchecked
return new SqmLiteral(
OffsetDateTime.of( localDate, localTime, offset ),
basicType( OffsetDateTime.class ),
creationContext.getNodeBuilder()
);
}
private SqmLiteral interpretJdbcDateTimeLiteral(HqlParser.JdbcTimestampLiteralContext jdbcDateTimeLiteralCtx) {
final Timestamp timestamp;
if ( jdbcDateTimeLiteralCtx.genericTemporalLiteralText() != null ) {
final TemporalAccessor parsed = DateTimeUtils.DATE_TIME.parseBest(
jdbcDateTimeLiteralCtx.genericTemporalLiteralText().STRING_LITERAL().getText(),
OffsetDateTime::from,
ZonedDateTime::from,
LocalDateTime::from
);
if ( parsed instanceof LocalDateTime ) {
final LocalDateTime localDateTime = (LocalDateTime) parsed;
//noinspection deprecation
timestamp = new Timestamp(
localDateTime.getYear(),
localDateTime.getMonthValue(),
localDateTime.getDayOfMonth(),
localDateTime.getHour(),
localDateTime.getMinute(),
localDateTime.getSecond(),
localDateTime.getNano()
);
}
else if ( parsed instanceof OffsetDateTime ) {
timestamp = new Timestamp( ( (OffsetDateTime) parsed ).toInstant().toEpochMilli() );
}
else {
assert parsed instanceof ZonedDateTime;
timestamp = new Timestamp( ( (ZonedDateTime) parsed ).toInstant().toEpochMilli() );
}
}
else {
final HqlParser.DateTimeContext dateTimeCtx = jdbcDateTimeLiteralCtx.dateTime();
assert dateTimeCtx != null;
final LocalDateTime localDateTime = LocalDateTime.of(
localDate( dateTimeCtx.date() ),
localTime( dateTimeCtx.time() )
);
if ( dateTimeCtx.zoneId() == null && dateTimeCtx.offset() == null ) {
// todo (6.0) : use local-tz or jdbc-tz as offset?
// for now, just use UTC
timestamp = new Timestamp( localDateTime.toInstant( ZoneOffset.UTC ).toEpochMilli() );
// below is another option
// timestamp = new Timestamp(
// localDateTime.getYear(),
// localDateTime.getMonthValue(),
// localDateTime.getDayOfMonth(),
// localDateTime.getHour(),
// localDateTime.getMinute(),
// localDateTime.getSecond(),
// localDateTime.getNano()
// );
}
else if ( dateTimeCtx.zoneId() != null ) {
// we have a "zone id", which could still identify an offset rather that a ZoneId
ZoneId zoneId;
try {
zoneId = ZoneId.of( dateTimeCtx.zoneId().STRING_LITERAL().getText() );
}
catch (Exception e) {
zoneId = TimeZone.getTimeZone( dateTimeCtx.zoneId().STRING_LITERAL().getText() ).toZoneId();
//noinspection unchecked
return new SqmLiteral(
ZonedDateTime.of( localDateTime, zoneId ),
basicType( OffsetDateTime.class ),
creationContext.getNodeBuilder()
);
}
timestamp = new Timestamp( ZonedDateTime.of( localDateTime, zoneId ).toInstant().toEpochMilli() );
}
else {
assert dateTimeCtx.offset() != null;
final HqlParser.OffsetContext offsetCtx = dateTimeCtx.offset();
assert offsetCtx != null;
// handle the offset
final int hourCtx = Integer.parseInt( offsetCtx.hour().INTEGER_LITERAL().getText() );
final int hour = offsetCtx.MINUS() == null
? hourCtx
: 0 - hourCtx;
final ZoneOffset offset = ZoneOffset.ofHoursMinutes(
hour,
offsetCtx.minute() == null
? 0
: Integer.parseInt( offsetCtx.minute().INTEGER_LITERAL().getText() )
);
timestamp = new Timestamp( OffsetDateTime.of( localDateTime, offset ).toInstant().toEpochMilli() );
}
}
//noinspection unchecked
return new SqmLiteral( timestamp, basicType( Timestamp.class ), creationContext.getNodeBuilder() );
}
private <J> BasicDomainType<J> basicType(Class<J> javaType) {
//noinspection unchecked
return creationContext.getJpaMetamodel().getTypeConfiguration().standardBasicTypeForJavaType( javaType );
}
private static LocalDate localDate(HqlParser.DateContext ctx) {
return LocalDate.of(
Integer.parseInt( ctx.year().getText() ),
Integer.parseInt( ctx.month().getText() ),
Integer.parseInt( ctx.day().getText() )
);
}
private static LocalTime localTime(HqlParser.TimeContext ctx) {
if ( ctx.second() != null ) {
int index = ctx.second().getText().indexOf('.');
if ( index < 0 ) {
return LocalTime.of(
Integer.parseInt( ctx.hour().getText() ),
Integer.parseInt( ctx.minute().getText() ),
Integer.parseInt( ctx.second().getText() )
);
}
else {
return LocalTime.of(
Integer.parseInt( ctx.hour().getText() ),
Integer.parseInt( ctx.minute().getText() ),
Integer.parseInt( ctx.second().getText().substring( 0, index ) ),
Integer.parseInt( ctx.second().getText().substring( index + 1 ) )
);
}
}
else {
return LocalTime.of(
Integer.parseInt( ctx.hour().getText() ),
Integer.parseInt( ctx.minute().getText() )
);
}
}
private SqmLiteral interpretDateLiteral(HqlParser.DateLiteralContext dateLiteral) {
//noinspection unchecked
return new SqmLiteral(
localDate( dateLiteral.date() ),
basicType( LocalDate.class ),
creationContext.getNodeBuilder()
);
}
private SqmLiteral interpretJdbcDateLiteral(HqlParser.JdbcDateLiteralContext dateLiteral) {
if ( dateLiteral.genericTemporalLiteralText() != null ) {
final LocalDate parsed = DateTimeUtils.DATE.parse(
dateLiteral.genericTemporalLiteralText().STRING_LITERAL().getText(),
LocalDate::from
);
//noinspection unchecked,deprecation
return new SqmLiteral(
new Date( parsed.getYear(), parsed.getMonthValue(), parsed.getDayOfMonth() ),
basicType( Date.class ),
creationContext.getNodeBuilder()
);
}
final HqlParser.DateContext dateCtx = dateLiteral.date();
//noinspection unchecked,deprecation
return new SqmLiteral(
new Date(
Integer.parseInt( dateCtx.year().INTEGER_LITERAL().getText() ),
Integer.parseInt( dateCtx.month().INTEGER_LITERAL().getText() ),
Integer.parseInt( dateCtx.day().INTEGER_LITERAL().getText() )
),
basicType( Date.class ),
creationContext.getNodeBuilder()
);
}
private SqmLiteral interpretTimeLiteral(HqlParser.TimeLiteralContext timeLiteral) {
//noinspection unchecked
return new SqmLiteral(
localTime( timeLiteral.time() ),
basicType( LocalTime.class ),
creationContext.getNodeBuilder()
);
}
private SqmLiteral interpretJdbcTimeLiteral(HqlParser.JdbcTimeLiteralContext timeLiteral) {
if ( timeLiteral.genericTemporalLiteralText() != null ) {
final LocalTime parsed = DateTimeUtils.TIME.parse(
timeLiteral.genericTemporalLiteralText().STRING_LITERAL().getText(),
LocalTime::from
);
//noinspection unchecked,deprecation
return new SqmLiteral(
new Time( parsed.getHour(), parsed.getMinute(), parsed.getSecond() ),
basicType( Time.class ),
creationContext.getNodeBuilder()
);
}
final int seconds;
if ( timeLiteral.time().second().FLOAT_LITERAL() == null ) {
seconds = Integer.parseInt( timeLiteral.time().second().INTEGER_LITERAL().getText() );
}
else {
final float floatValue = Float.parseFloat( timeLiteral.time().second().FLOAT_LITERAL().getText() );
log.debugf( "Float-value encountered for seconds part of JDBC Date literal - ignoring fractional : " + floatValue );
seconds = (int) floatValue;
}
//noinspection unchecked,deprecation
return new SqmLiteral(
new Time(
Integer.parseInt( timeLiteral.time().hour().INTEGER_LITERAL().getText() ),
Integer.parseInt( timeLiteral.time().minute().INTEGER_LITERAL().getText() ),
seconds
),
basicType( Time.class ),
creationContext.getNodeBuilder()
);
}
@Override
public SqmLiteral visitGeneralizedLiteral(HqlParser.GeneralizedLiteralContext ctx) {
throw new NotYetImplementedFor6Exception( getClass() );
}
private SqmLiteral<Boolean> booleanLiteral(boolean value) { private SqmLiteral<Boolean> booleanLiteral(boolean value) {
final SqmExpressable expressionType = resolveExpressableTypeBasic( Boolean.class ); final SqmExpressable expressionType = resolveExpressableTypeBasic( Boolean.class );
//noinspection unchecked //noinspection unchecked
return new SqmLiteral<>( value, expressionType, creationContext.getQueryEngine().getCriteriaBuilder() ); return new SqmLiteral<>( value, expressionType, creationContext.getQueryEngine().getCriteriaBuilder() );
} }
private SqmLiteral<Character> characterLiteral(String text) {
if ( text.length() > 1 ) {
// todo : or just treat it as a String literal?
throw new ParsingException( "Value for CHARACTER_LITERAL token was more than 1 character" );
}
return new SqmLiteral<>(
text.charAt( 0 ),
resolveExpressableTypeBasic( Character.class ),
creationContext.getNodeBuilder()
);
}
private SqmLiteral<String> stringLiteral(String text) { private SqmLiteral<String> stringLiteral(String text) {
return new SqmLiteral<>( return new SqmLiteral<>(
text, text,
@ -2208,7 +2547,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
@Override @Override
public Object visitDayField(HqlParser.DayFieldContext ctx) { public Object visitDayField(HqlParser.DayFieldContext ctx) {
NodeBuilder nodeBuilder = creationContext.getNodeBuilder(); final NodeBuilder nodeBuilder = creationContext.getNodeBuilder();
if ( ctx.WEEK() != null ) { if ( ctx.WEEK() != null ) {
return new SqmExtractUnit<>( DAY_OF_WEEK, resolveExpressableTypeBasic( Integer.class ), nodeBuilder ); return new SqmExtractUnit<>( DAY_OF_WEEK, resolveExpressableTypeBasic( Integer.class ), nodeBuilder );
} }
@ -2223,7 +2562,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
@Override @Override
public Object visitWeekField(HqlParser.WeekFieldContext ctx) { public Object visitWeekField(HqlParser.WeekFieldContext ctx) {
NodeBuilder nodeBuilder = creationContext.getNodeBuilder(); final NodeBuilder nodeBuilder = creationContext.getNodeBuilder();
if ( ctx.MONTH() != null ) { if ( ctx.MONTH() != null ) {
//this is computed from DAY_OF_MONTH/7 //this is computed from DAY_OF_MONTH/7
return new SqmExtractUnit<>( WEEK_OF_MONTH, resolveExpressableTypeBasic( Integer.class ), nodeBuilder ); return new SqmExtractUnit<>( WEEK_OF_MONTH, resolveExpressableTypeBasic( Integer.class ), nodeBuilder );
@ -2270,27 +2609,245 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
private boolean isExtractingJdbcTemporalType; private boolean isExtractingJdbcTemporalType;
@Override @Override
public Object visitExtractFunction(HqlParser.ExtractFunctionContext ctx) { public Object visitExtractFunction(HqlParser.ExtractFunctionContext extractFunctionCtx) {
final SqmExpression<?> expressionToExtract = (SqmExpression) ctx.expression().accept( this ); final SqmExpression<?> sqmTemporalExpr = (SqmExpression) extractFunctionCtx.expression().accept( this );
final SqmExtractUnit<?> extractFieldExpression; final SqmExtractUnit<?> sqmExtractUnit;
if ( ctx.extractField() != null ) { // Allow `#visitDateOrTimeField()` to know if we're extracting from a JDBC Timestamp or from a
extractFieldExpression = (SqmExtractUnit) ctx.extractField().accept( this); // java.time LocalDateTime/OffsetDateTime
isExtractingJdbcTemporalType = isJdbcTemporalType( sqmTemporalExpr.getNodeType() );
if ( extractFunctionCtx.extractField() != null ) {
// for the case of the full ANSI syntax "extract(field from arg)"
sqmExtractUnit = (SqmExtractUnit) extractFunctionCtx.extractField().accept( this);
} }
else if ( ctx.datetimeField() != null ) { else if ( extractFunctionCtx.datetimeField() != null ) {
isExtractingJdbcTemporalType = isJdbcTemporalType( expressionToExtract.getNodeType() ); // for the shorter legacy Hibernate syntax "field(arg)"
extractFieldExpression = (SqmExtractUnit) ctx.datetimeField().accept( this ); sqmExtractUnit = (SqmExtractUnit) extractFunctionCtx.datetimeField().accept( this );
} }
else { else {
return expressionToExtract; return sqmTemporalExpr;
} }
final TemporalUnit unit = sqmExtractUnit.getTemporalUnit();
switch ( unit ) {
case NANOSECOND: {
return extractNanoseconds( sqmTemporalExpr );
}
case OFFSET: {
// use formatdatetime(arg, 'xxx') to get the offset
return extractOffsetUsingFormat( sqmTemporalExpr );
}
case DATE:
case TIME: {
// use cast(arg as Type) to get the date or time part
// which might be javax.sql.Date / javax.sql.Time or
// java.time.LocalDate / java.time.LocalTime depending
// on the type of the expression we're extracting from
return extractDateOrTimeUsingCast(
sqmTemporalExpr,
sqmExtractUnit.getType()
);
}
case WEEK_OF_MONTH: {
// use ceiling(extract(day of month, arg)/7.0)
return extractWeek( sqmTemporalExpr, DAY_OF_MONTH );
}
case WEEK_OF_YEAR: {
// use ceiling(extract(day of year, arg)/7.0)
return extractWeek( sqmTemporalExpr, DAY_OF_YEAR );
}
default: {
// otherwise it's something we expect the SQL dialect
// itself to understand, either natively, or via the
// registered function template for extract()
//noinspection unchecked //noinspection unchecked
return new SqmFunction( return new SqmFunction(
"extract", "extract",
getFunctionDescriptor( "extract" ), getFunctionDescriptor( "extract" ),
extractFieldExpression.getNodeType(), sqmExtractUnit.getNodeType(),
asList( extractFieldExpression, expressionToExtract ), asList( sqmExtractUnit, sqmTemporalExpr ),
creationContext.getNodeBuilder()
);
}
}
}
private SqmExpression<Long> extractNanoseconds(SqmExpression<?> sqmTemporalExpr) {
final SqmFunctionDescriptor roundFunctionDescriptor = getFunctionDescriptor( "round" );
final SqmFunctionDescriptor extractFunctionDescriptor = getFunctionDescriptor( "extract" );
final SqmLiteral<Float> nanoMultiplierLiteral = floatLiteral( "1e9" );
final SqmLiteral<Integer> zeroLiteral = integerLiteral( "0" );
final SqmSelfRenderingExpression<Long> sqmExtractSeconds = new SqmSelfRenderingExpression<>(
semanticQueryWalker -> extractFunctionDescriptor.generateSqlExpression(
"extract",
asList(
new SqmExtractUnit<>(
SECOND,
StandardBasicTypes.INTEGER,
creationContext.getNodeBuilder()
),
sqmTemporalExpr
),
() -> (MappingModelExpressable) basicType( Long.class ),
(SqmToSqlAstConverter) semanticQueryWalker,
(SqlAstCreationState) semanticQueryWalker
),
basicType( Long.class ),
creationContext.getNodeBuilder()
);
return new SqmSelfRenderingExpression<>(
semanticQueryWalker -> roundFunctionDescriptor.generateSqlExpression(
"round",
asList(
new SqmBinaryArithmetic<>(
BinaryArithmeticOperator.MULTIPLY,
sqmExtractSeconds,
nanoMultiplierLiteral,
basicType( Float.class ),
creationContext.getNodeBuilder()
),
zeroLiteral
),
() -> (MappingModelExpressable) basicType( Float.class ),
(SqmToSqlAstConverter) semanticQueryWalker,
(SqlAstCreationState) semanticQueryWalker
),
basicType( Long.class ),
creationContext.getNodeBuilder()
);
}
private SqmExpression<?> extractDateOrTimeUsingCast(
SqmExpression<?> expressionToExtract,
AllowableFunctionReturnType<?> type) {
return new SqmFunction<>(
"cast",
getFunctionDescriptor( "cast" ),
type,
asList(
expressionToExtract,
new SqmCastTarget<>(
type,
creationContext.getNodeBuilder()
)
),
creationContext.getNodeBuilder()
);
}
private SqmExpression<ZoneOffset> extractOffsetUsingFormat(SqmExpression<?> expressionToExtract) {
return new SqmFunction<>(
"formatdatetime",
getFunctionDescriptor( "formatdatetime" ),
basicType( ZoneOffset.class ),
asList(
expressionToExtract,
new SqmFormat(
"xxx", //pattern for timezone offset
basicType( String.class ),
creationContext.getNodeBuilder()
)
),
creationContext.getNodeBuilder()
);
}
private SqmExpression<Integer> extractWeek(
SqmExpression<?> expressionToExtract,
TemporalUnit dayOf) {
final BasicDomainType<Integer> intType = basicType( Integer.class );
final SqmFunctionDescriptor extractFunctionDescriptor = getFunctionDescriptor( "extract" );
final SqmFunction<Integer> extractDayOf = new SqmFunction<>(
"extract",
extractFunctionDescriptor,
intType,
asList(
new SqmExtractUnit<>(
dayOf,
intType,
creationContext.getNodeBuilder()
),
expressionToExtract
),
creationContext.getNodeBuilder()
);
final SqmFunction<Integer> extractDayOfWeek = new SqmFunction<>(
"extract",
extractFunctionDescriptor,
intType,
asList(
new SqmExtractUnit<>(
DAY_OF_WEEK,
intType,
creationContext.getNodeBuilder()
),
expressionToExtract
),
creationContext.getNodeBuilder()
);
final SqmBinaryArithmetic<Integer> subtraction = new SqmBinaryArithmetic<>(
BinaryArithmeticOperator.SUBTRACT,
extractDayOf,
extractDayOfWeek,
intType,
creationContext.getNodeBuilder()
);
final SqmBinaryArithmetic<Float> division = new SqmBinaryArithmetic<>(
BinaryArithmeticOperator.DIVIDE,
subtraction,
floatLiteral( "7.0" ),
basicType( Float.class ),
creationContext.getNodeBuilder()
);
return new SqmBinaryArithmetic<>(
BinaryArithmeticOperator.ADD,
new SqmFunction<>(
"ceiling",
getFunctionDescriptor( "ceiling" ),
intType,
Collections.singletonList( division ),
creationContext.getNodeBuilder()
),
integerLiteral("1"),
intType,
creationContext.getNodeBuilder()
);
}
@Override
public SqmFunction visitFormatFunction(HqlParser.FormatFunctionContext ctx) {
final SqmExpression<?> expressionToCast = (SqmExpression) ctx.expression().accept( this );
final SqmFormat format = visitFormat( ctx.format() );
return new SqmFunction<>(
"formatdatetime",
getFunctionDescriptor( "formatdatetime" ),
basicType( String.class ),
asList( expressionToCast, format ),
creationContext.getNodeBuilder()
);
}
@Override
public SqmFormat visitFormat(HqlParser.FormatContext ctx) {
final String formatPattern = ctx.STRING_LITERAL().getText();
// if (!FORMAT.matcher(format).matches()) {
// throw new SemanticException("illegal format pattern: '" + format + "'");
// }
return new SqmFormat(
formatPattern,
basicType( String.class ),
creationContext.getNodeBuilder() creationContext.getNodeBuilder()
); );
} }
@ -2633,20 +3190,6 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
@Override @Override
public SqmLiteral<Character> visitTrimCharacter(HqlParser.TrimCharacterContext ctx) { public SqmLiteral<Character> visitTrimCharacter(HqlParser.TrimCharacterContext ctx) {
// todo (6.0) : we should delay this until we are walking the SQM
if ( ctx.CHARACTER_LITERAL() != null ) {
final String trimCharText = ctx.CHARACTER_LITERAL().getText();
if ( trimCharText.length() != 1 ) {
throw new SemanticException( "Expecting [trim character] for TRIM function to be single character, found : " + trimCharText );
}
return new SqmLiteral<>(
trimCharText.charAt( 0 ),
resolveExpressableTypeBasic( Character.class ),
creationContext.getNodeBuilder()
);
}
if ( ctx.STRING_LITERAL() != null ) { if ( ctx.STRING_LITERAL() != null ) {
final String trimCharText = ctx.STRING_LITERAL().getText(); final String trimCharText = ctx.STRING_LITERAL().getText();
if ( trimCharText.length() != 1 ) { if ( trimCharText.length() != 1 ) {
@ -2971,6 +3514,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
} }
} }
//noinspection unchecked
SqmPath result = attribute.getElementPathSource().createSqmPath( SqmPath result = attribute.getElementPathSource().createSqmPath(
pluralAttributePath, pluralAttributePath,
this this

View File

@ -69,9 +69,11 @@ import org.hibernate.query.sqm.tree.expression.SqmEnumLiteral;
import org.hibernate.query.sqm.tree.expression.SqmExpression; import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.expression.SqmExtractUnit; import org.hibernate.query.sqm.tree.expression.SqmExtractUnit;
import org.hibernate.query.sqm.tree.expression.SqmFieldLiteral; import org.hibernate.query.sqm.tree.expression.SqmFieldLiteral;
import org.hibernate.query.sqm.tree.expression.SqmFormat;
import org.hibernate.query.sqm.tree.expression.SqmFunction; import org.hibernate.query.sqm.tree.expression.SqmFunction;
import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper; import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper;
import org.hibernate.query.sqm.tree.expression.SqmLiteral; import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.query.sqm.tree.expression.SqmLiteralNull;
import org.hibernate.query.sqm.tree.expression.SqmNamedParameter; import org.hibernate.query.sqm.tree.expression.SqmNamedParameter;
import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.expression.SqmPositionalParameter; import org.hibernate.query.sqm.tree.expression.SqmPositionalParameter;
@ -125,6 +127,7 @@ import org.hibernate.sql.ast.tree.expression.CastTarget;
import org.hibernate.sql.ast.tree.expression.Distinct; import org.hibernate.sql.ast.tree.expression.Distinct;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.ExtractUnit; import org.hibernate.sql.ast.tree.expression.ExtractUnit;
import org.hibernate.sql.ast.tree.expression.Format;
import org.hibernate.sql.ast.tree.expression.QueryLiteral; import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.expression.Star; import org.hibernate.sql.ast.tree.expression.Star;
import org.hibernate.sql.ast.tree.expression.TrimSpecification; import org.hibernate.sql.ast.tree.expression.TrimSpecification;
@ -781,6 +784,10 @@ public abstract class BaseSqmToSqlAstConverter
@Override @Override
public Expression visitLiteral(SqmLiteral literal) { public Expression visitLiteral(SqmLiteral literal) {
if ( literal instanceof SqmLiteralNull ) {
return new QueryLiteral( null, (BasicValuedMapping) inferableTypeAccessStack.getCurrent().get() );
}
return new QueryLiteral( return new QueryLiteral(
literal.getLiteralValue(), literal.getLiteralValue(),
(BasicValuedMapping) SqmMappingModelHelper.resolveMappingModelExpressable( (BasicValuedMapping) SqmMappingModelHelper.resolveMappingModelExpressable(
@ -939,13 +946,11 @@ public abstract class BaseSqmToSqlAstConverter
@Override @Override
public Expression visitFunction(SqmFunction sqmFunction) { public Expression visitFunction(SqmFunction sqmFunction) {
final SqmFunctionDescriptor functionDescriptor = creationContext.getSessionFactory() final SqmFunctionDescriptor functionDescriptor = sqmFunction.getFunctionDescriptor();
.getQueryEngine()
.getSqmFunctionRegistry()
.getFunctionDescriptor( sqmFunction.getFunctionName() );
shallownessStack.push( Shallowness.FUNCTION ); shallownessStack.push( Shallowness.FUNCTION );
try { try {
//noinspection unchecked
return functionDescriptor.generateSqlExpression( return functionDescriptor.generateSqlExpression(
sqmFunction.getFunctionName(), sqmFunction.getFunctionName(),
sqmFunction.getArguments(), sqmFunction.getArguments(),
@ -1009,6 +1014,14 @@ public abstract class BaseSqmToSqlAstConverter
} }
} }
@Override
public Object visitFormat(SqmFormat sqmFormat) {
return new Format(
sqmFormat.getLiteralValue(),
(BasicValuedMapping) sqmFormat.getNodeType()
);
}
// @Override // @Override
// public Object visitAbsFunction(SqmAbsFunction function) { // public Object visitAbsFunction(SqmAbsFunction function) {
// shallownessStack.push( Shallowness.FUNCTION ); // shallownessStack.push( Shallowness.FUNCTION );
@ -1425,7 +1438,7 @@ public abstract class BaseSqmToSqlAstConverter
return new BinaryArithmeticExpression( return new BinaryArithmeticExpression(
(Expression) expression.getLeftHandOperand().accept( this ), interpret( expression.getOperator() ), (Expression) expression.getLeftHandOperand().accept( this ), interpret( expression.getOperator() ),
(Expression) expression.getRightHandOperand().accept( this ), (Expression) expression.getRightHandOperand().accept( this ),
determineValueMapping( expression ) (BasicValuedMapping) determineValueMapping( expression )
); );
} }
finally { finally {

View File

@ -13,13 +13,13 @@ import java.util.function.Consumer;
import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath;
import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.SqlSelectionExpression; import org.hibernate.sql.ast.tree.expression.SqlSelectionExpression;
@ -100,13 +100,11 @@ public class BasicValuedPathInterpretation<T> implements AssignableSqmPathInterp
assert tableGroup != null; assert tableGroup != null;
this.tableGroup = tableGroup; this.tableGroup = tableGroup;
} }
@Override @Override
public SqmPath<T> getInterpretedSqmPath() { public NavigablePath getNavigablePath() {
return sqmPath; return sqmPath.getNavigablePath();
} }
@Override @Override

View File

@ -13,6 +13,7 @@ import java.util.function.Consumer;
import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
@ -78,8 +79,8 @@ public class EmbeddableValuedPathInterpretation<T> implements AssignableSqmPathI
} }
@Override @Override
public SqmPath<T> getInterpretedSqmPath() { public NavigablePath getNavigablePath() {
return sqmPath; return sqmPath.getNavigablePath();
} }
@Override @Override

View File

@ -8,6 +8,7 @@ package org.hibernate.query.sqm.sql.internal;
import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlAstCreationState;
@ -62,13 +63,13 @@ public class EntityValuedPathInterpretation<T> implements SqmPathInterpretation<
} }
@Override @Override
public SqmPath<T> getInterpretedSqmPath() { public NavigablePath getNavigablePath() {
return sqmPath; return sqmPath.getNavigablePath();
} }
@Override @Override
public ModelPart getExpressionType() { public ModelPart getExpressionType() {
return null; return mapping;
} }
@Override @Override

View File

@ -16,16 +16,11 @@ import org.hibernate.sql.ast.tree.expression.Expression;
* for path interpretations because it can (and likely) contains multiple SqlExpressions (entity to its columns, e.g.) * for path interpretations because it can (and likely) contains multiple SqlExpressions (entity to its columns, e.g.)
* *
* @see org.hibernate.query.sqm.sql.SqmToSqlAstConverter * @see org.hibernate.query.sqm.sql.SqmToSqlAstConverter
* @see #getInterpretedSqmPath
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface SqmPathInterpretation<T> extends Expression, DomainResultProducer<T> { public interface SqmPathInterpretation<T> extends Expression, DomainResultProducer<T> {
default NavigablePath getNavigablePath() { NavigablePath getNavigablePath();
return getInterpretedSqmPath().getNavigablePath();
}
SqmPath<T> getInterpretedSqmPath();
@Override @Override
ModelPart getExpressionType(); ModelPart getExpressionType();

View File

@ -55,6 +55,10 @@ public class SqmFunction<T> extends AbstractSqmExpression<T> implements JpaFunct
this.arguments = arguments; this.arguments = arguments;
} }
public SqmFunctionDescriptor getFunctionDescriptor() {
return functionDescriptor;
}
@Override @Override
public String getFunctionName() { public String getFunctionName() {
return functionName; return functionName;

View File

@ -0,0 +1,35 @@
/*
* 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.query.sqm.tree.expression;
import java.util.function.Function;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.SqmExpressable;
import org.hibernate.sql.ast.tree.expression.Expression;
/**
* @author Steve Ebersole
*/
public class SqmSelfRenderingExpression<T> extends AbstractSqmExpression<T> implements SqmExpression<T> {
private final Function<SemanticQueryWalker, Expression> renderer;
public SqmSelfRenderingExpression(
Function<SemanticQueryWalker, Expression> renderer,
SqmExpressable<T> type,
NodeBuilder criteriaBuilder) {
super( type, criteriaBuilder );
this.renderer = renderer;
}
@Override
public <X> X accept(SemanticQueryWalker<X> walker) {
//noinspection unchecked
return (X) renderer.apply( walker );
}
}

View File

@ -23,6 +23,8 @@ public class SqmSelection<T> extends AbstractSqmNode implements SqmAliasedNode<T
SqmSelectableNode<T> selectableNode, SqmSelectableNode<T> selectableNode,
NodeBuilder nodeBuilder) { NodeBuilder nodeBuilder) {
super( nodeBuilder ); super( nodeBuilder );
assert selectableNode != null;
this.selectableNode = selectableNode; this.selectableNode = selectableNode;
} }
@ -31,6 +33,8 @@ public class SqmSelection<T> extends AbstractSqmNode implements SqmAliasedNode<T
String alias, String alias,
NodeBuilder nodeBuilder) { NodeBuilder nodeBuilder) {
super( nodeBuilder ); super( nodeBuilder );
assert selectableNode != null;
this.selectableNode = selectableNode; this.selectableNode = selectableNode;
selectableNode.alias( alias ); selectableNode.alias( alias );
} }

View File

@ -65,6 +65,7 @@ import org.hibernate.sql.ast.tree.select.SortSpecification;
import org.hibernate.sql.exec.internal.JdbcParametersImpl; import org.hibernate.sql.exec.internal.JdbcParametersImpl;
import org.hibernate.sql.exec.spi.JdbcParameterBinder; import org.hibernate.sql.exec.spi.JdbcParameterBinder;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators; import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
import org.hibernate.type.descriptor.sql.JdbcLiteralFormatter;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.sql.ast.spi.SqlAppender.CLOSE_PARENTHESIS; import static org.hibernate.sql.ast.spi.SqlAppender.CLOSE_PARENTHESIS;
@ -803,9 +804,11 @@ public abstract class AbstractSqlAstWalker
@Override @Override
public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) {
appendSql( "(" );
arithmeticExpression.getLeftHandOperand().accept( this ); arithmeticExpression.getLeftHandOperand().accept( this );
appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() ); appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() );
arithmeticExpression.getRightHandOperand().accept( this ); arithmeticExpression.getRightHandOperand().accept( this );
appendSql( ")" );
} }
@Override @Override

View File

@ -13,6 +13,7 @@ import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.mutation.internal.cte.CteStrategy; import org.hibernate.query.sqm.mutation.internal.cte.CteStrategy;
@ -52,6 +53,11 @@ public class CteTableGroup implements TableGroup {
return null; return null;
} }
@Override
public ModelPart getExpressionType() {
return getModelPart();
}
@Override @Override
public Set<TableGroupJoin> getTableGroupJoins() { public Set<TableGroupJoin> getTableGroupJoins() {
return Collections.emptySet(); return Collections.emptySet();

View File

@ -6,10 +6,17 @@
*/ */
package org.hibernate.sql.ast.tree.expression; package org.hibernate.sql.ast.tree.expression;
import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.query.BinaryArithmeticOperator; import org.hibernate.query.BinaryArithmeticOperator;
import org.hibernate.query.sqm.sql.internal.DomainResultProducer; import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.sql.results.internal.SqlSelectionImpl;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
@ -20,13 +27,13 @@ public class BinaryArithmeticExpression implements Expression, DomainResultProdu
private final BinaryArithmeticOperator operator; private final BinaryArithmeticOperator operator;
private final Expression rhsOperand; private final Expression rhsOperand;
private final MappingModelExpressable resultType; private final BasicValuedMapping resultType;
public BinaryArithmeticExpression( public BinaryArithmeticExpression(
Expression lhsOperand, Expression lhsOperand,
BinaryArithmeticOperator operator, BinaryArithmeticOperator operator,
Expression rhsOperand, Expression rhsOperand,
MappingModelExpressable resultType) { BasicValuedMapping resultType) {
this.operator = operator; this.operator = operator;
this.lhsOperand = lhsOperand; this.lhsOperand = lhsOperand;
this.rhsOperand = rhsOperand; this.rhsOperand = rhsOperand;
@ -34,7 +41,7 @@ public class BinaryArithmeticExpression implements Expression, DomainResultProdu
} }
@Override @Override
public MappingModelExpressable getExpressionType() { public BasicValuedMapping getExpressionType() {
return resultType; return resultType;
} }
@ -43,22 +50,37 @@ public class BinaryArithmeticExpression implements Expression, DomainResultProdu
walker.visitBinaryArithmeticExpression( this ); walker.visitBinaryArithmeticExpression( this );
} }
// @Override @Override
// public DomainResult createDomainResult( public DomainResult createDomainResult(
// String resultVariable, String resultVariable,
// DomainResultCreationState creationState) { DomainResultCreationState creationState) {
// final SqlSelection sqlSelection = creationState.getSqlExpressionResolver().resolveSqlSelection( final SqlSelection sqlSelection = creationState.getSqlAstCreationState().getSqlExpressionResolver().resolveSqlSelection(
// this, this,
// getType().getJavaTypeDescriptor(), resultType.getBasicType().getJavaTypeDescriptor(),
// creationState.getSqlAstCreationState().getCreationContext().getDomainModel().getTypeConfiguration() creationState.getSqlAstCreationState().getCreationContext().getDomainModel().getTypeConfiguration()
// ); );
// //noinspection unchecked
// return new ScalarDomainResultImpl( //noinspection unchecked
// sqlSelection.getValuesArrayPosition(), return new BasicResult(
// resultVariable, sqlSelection.getValuesArrayPosition(),
// resultType.getJavaTypeDescriptor() resultVariable,
// ); resultType.getBasicType().getJavaTypeDescriptor()
// } );
}
@Override
public SqlSelection createSqlSelection(
int jdbcPosition,
int valuesArrayPosition,
JavaTypeDescriptor javaTypeDescriptor,
TypeConfiguration typeConfiguration) {
return new SqlSelectionImpl(
jdbcPosition,
valuesArrayPosition,
this,
resultType.getJdbcMapping()
);
}
/** /**
* Get the left-hand operand. * Get the left-hand operand.

View File

@ -9,11 +9,11 @@ package org.hibernate.sql.ast.tree.from;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.spi.SqlAliasBase;
@ -21,7 +21,6 @@ import org.hibernate.sql.ast.spi.SqlAliasBase;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifier implements TableGroup { public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifier implements TableGroup {
private final NavigablePath navigablePath; private final NavigablePath navigablePath;
private final TableGroupProducer producer; private final TableGroupProducer producer;
private final LockMode lockMode; private final LockMode lockMode;
@ -78,6 +77,11 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie
return producer; return producer;
} }
@Override
public ModelPart getExpressionType() {
return getModelPart();
}
@Override @Override
public LockMode getLockMode() { public LockMode getLockMode() {
return lockMode; return lockMode;

View File

@ -15,6 +15,7 @@ import java.util.function.Supplier;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
@ -43,6 +44,11 @@ public class CompositeTableGroup implements VirtualTableGroup {
return navigablePath; return navigablePath;
} }
@Override
public EmbeddableValuedModelPart getExpressionType() {
return getModelPart();
}
@Override @Override
public String getGroupAlias() { public String getGroupAlias() {
// none, although we could also delegate to the underlyingTableGroup's group-alias // none, although we could also delegate to the underlyingTableGroup's group-alias
@ -50,7 +56,7 @@ public class CompositeTableGroup implements VirtualTableGroup {
} }
@Override @Override
public ModelPartContainer getModelPart() { public EmbeddableValuedModelPart getModelPart() {
return compositionMapping; return compositionMapping;
} }

View File

@ -13,6 +13,7 @@ import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
@ -41,6 +42,11 @@ public class MutatingTableReferenceGroupWrapper implements VirtualTableGroup {
return navigablePath; return navigablePath;
} }
@Override
public ModelPart getExpressionType() {
return getModelPart();
}
@Override @Override
public String getGroupAlias() { public String getGroupAlias() {
return null; return null;

View File

@ -14,6 +14,7 @@ import org.hibernate.LockMode;
import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.sql.internal.DomainResultProducer; import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation;
import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.ColumnReference;
@ -26,7 +27,7 @@ import org.hibernate.sql.results.graph.DomainResultCreationState;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface TableGroup extends SqlAstNode, ColumnReferenceQualifier, DomainResultProducer { public interface TableGroup extends SqlAstNode, ColumnReferenceQualifier, SqmPathInterpretation, DomainResultProducer {
NavigablePath getNavigablePath(); NavigablePath getNavigablePath();
/** /**

View File

@ -13,6 +13,7 @@ import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.persister.entity.UnionSubclassEntityPersister; import org.hibernate.persister.entity.UnionSubclassEntityPersister;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
@ -40,6 +41,11 @@ public class UnionTableGroup implements VirtualTableGroup {
return navigablePath; return navigablePath;
} }
@Override
public ModelPart getExpressionType() {
return getModelPart();
}
@Override @Override
public String getGroupAlias() { public String getGroupAlias() {
return null; return null;

View File

@ -12,6 +12,7 @@ import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart; import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.ast.SqlAstWalker;
@ -42,6 +43,11 @@ public class EntityCollectionPartTableGroup implements TableGroup {
return collectionPartPath; return collectionPartPath;
} }
@Override
public ModelPart getExpressionType() {
return getModelPart();
}
@Override @Override
public String getGroupAlias() { public String getGroupAlias() {
return null; return null;

View File

@ -7,10 +7,16 @@
package org.hibernate.type.descriptor; package org.hibernate.type.descriptor;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField; import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQueries;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
@ -61,6 +67,16 @@ public final class DateTimeUtils {
.optionalStart().appendZoneOrOffsetId().optionalEnd() .optionalStart().appendZoneOrOffsetId().optionalEnd()
.toFormatter(); .toFormatter();
/**
* Pattern used for parsing literal dates in HQL.
*/
public static final DateTimeFormatter DATE = ISO_LOCAL_DATE;
/**
* Pattern used for parsing literal times in HQL.
*/
public static final DateTimeFormatter TIME = ISO_LOCAL_TIME;
/** /**
* Pattern used for parsing literal offset datetimes in HQL. * Pattern used for parsing literal offset datetimes in HQL.
* *
@ -170,4 +186,79 @@ public final class DateTimeUtils {
return formatter; return formatter;
} }
public static void main(String... args) {
final ZoneId localTzId = ZoneId.systemDefault();
System.out.printf( "Local tz : %s\n", localTzId );
final String[] values = new String[] {
"1999-12-31 12:59:59.3",
"1999-12-31 12:59:59 +02:00",
"1999-12-31 12:59:59 UTC",
"1999-12-31 12:59:59 UTC+02:00",
"1999-12-31 12:59:59 " + localTzId.getId()
};
for ( String value : values ) {
final TemporalAccessor parsed = DATE_TIME.parseBest(
value,
OffsetDateTime::from,
ZonedDateTime::from,
LocalDateTime::from
);
System.out.println( value + " -> " + parsed + " (" + parsed.getClass().getName() + ")" );
final ZonedDateTime zdt;
if ( parsed instanceof LocalDateTime ) {
// here, "localTzId" would come from the "jdbc timezone" setting?
zdt = ( (LocalDateTime) parsed ).atZone( localTzId );
System.out.println( " - LocalDateTime adjusted to ZonedDateTime : " + parsed );
}
else if ( parsed instanceof OffsetDateTime ) {
zdt = ( (OffsetDateTime) parsed ).toZonedDateTime();
System.out.println( " - OffsetDateTime adjusted to ZonedDateTime : " + parsed );
}
else {
zdt = (ZonedDateTime) parsed;
}
System.out.println( " - ZoneId = " + zdt.getZone().getId() );
System.out.println( " - offset = " + zdt.getOffset() );
System.out.println( " - normalized = " + zdt.getZone().normalized() );
final ZonedDateTime adjusted = zdt.withZoneSameInstant( localTzId );
System.out.println( " - adjusted = " + adjusted.toLocalDateTime() + " (zone-id:" + adjusted.getZone() + ")" );
}
}
public static TemporalAccessor transform(String text) {
return DATE_TIME.parse(
text,
(temporal) -> {
// see if there is an offset or tz
final ZoneId zoneOrOffset = temporal.query( TemporalQueries.zone() );
if ( zoneOrOffset != null ) {
final ZonedDateTime zdt = ZonedDateTime.from( temporal );
// EDIT: call normalized() to convert a ZoneId
// with constant offset, e.g., UTC+02:00, to ZoneOffset
if ( zoneOrOffset.normalized() instanceof ZoneOffset ) {
return zdt.toOffsetDateTime();
}
else {
return zdt;
}
}
// otherwise it's a LocalDateTime
return LocalDateTime.from( temporal );
}
);
}
public static OffsetDateTime usingOffset(String text) {
// does not work
final TemporalAccessor parsed = DATE_TIME.parse( text );
return OffsetDateTime.from( parsed );
}
} }

View File

@ -15,7 +15,10 @@ import java.sql.Types;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.internal.JdbcLiteralFormatterNumericData;
import org.hibernate.type.spi.TypeConfiguration;
/** /**
* Descriptor for {@link Types#BIGINT BIGINT} handling. * Descriptor for {@link Types#BIGINT BIGINT} handling.
@ -38,6 +41,17 @@ public class BigIntTypeDescriptor implements SqlTypeDescriptor {
return true; return true;
} }
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Long.class );
}
@Override
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaTypeDescriptor<T> javaTypeDescriptor) {
//noinspection unchecked
return new JdbcLiteralFormatterNumericData( javaTypeDescriptor, Long.class );
}
@Override @Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) { return new BasicBinder<X>( javaTypeDescriptor, this ) {

View File

@ -15,7 +15,9 @@ import java.sql.Types;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/** /**
* Descriptor for {@link Types#BIT BIT} handling. * Descriptor for {@link Types#BIT BIT} handling.
@ -40,6 +42,11 @@ public class BitTypeDescriptor implements SqlTypeDescriptor {
return true; return true;
} }
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Boolean.class );
}
@Override @Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) { return new BasicBinder<X>( javaTypeDescriptor, this ) {

View File

@ -16,7 +16,9 @@ import java.sql.Types;
import org.hibernate.engine.jdbc.BinaryStream; import org.hibernate.engine.jdbc.BinaryStream;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/** /**
* Descriptor for {@link Types#BLOB BLOB} handling. * Descriptor for {@link Types#BLOB BLOB} handling.
@ -40,6 +42,11 @@ public abstract class BlobTypeDescriptor implements SqlTypeDescriptor {
return true; return true;
} }
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Blob.class );
}
@Override @Override
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) { public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) { return new BasicExtractor<X>( javaTypeDescriptor, this ) {

View File

@ -15,7 +15,10 @@ import java.sql.Types;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.internal.JdbcLiteralFormatterBoolean;
import org.hibernate.type.spi.TypeConfiguration;
/** /**
* Descriptor for {@link java.sql.Types#BOOLEAN BOOLEAN} handling. * Descriptor for {@link java.sql.Types#BOOLEAN BOOLEAN} handling.
@ -37,6 +40,17 @@ public class BooleanTypeDescriptor implements SqlTypeDescriptor {
return true; return true;
} }
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Boolean.class );
}
@Override
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaTypeDescriptor<T> javaTypeDescriptor) {
//noinspection unchecked
return new JdbcLiteralFormatterBoolean( javaTypeDescriptor );
}
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) { return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override @Override

View File

@ -14,10 +14,15 @@ import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import java.util.Calendar; import java.util.Calendar;
import javax.persistence.TemporalType;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.internal.JdbcLiteralFormatterTemporal;
import org.hibernate.type.spi.TypeConfiguration;
/** /**
* Descriptor for {@link Types#DATE DATE} handling. * Descriptor for {@link Types#DATE DATE} handling.
@ -40,6 +45,17 @@ public class DateTypeDescriptor implements SqlTypeDescriptor {
return true; return true;
} }
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Date.class );
}
@Override
@SuppressWarnings("unchecked")
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaTypeDescriptor<T> javaTypeDescriptor) {
return new JdbcLiteralFormatterTemporal( javaTypeDescriptor, TemporalType.DATE );
}
@Override @Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) { return new BasicBinder<X>( javaTypeDescriptor, this ) {

View File

@ -16,7 +16,10 @@ import java.sql.Types;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.internal.JdbcLiteralFormatterNumericData;
import org.hibernate.type.spi.TypeConfiguration;
/** /**
* Descriptor for {@link Types#DECIMAL DECIMAL} handling. * Descriptor for {@link Types#DECIMAL DECIMAL} handling.
@ -39,6 +42,17 @@ public class DecimalTypeDescriptor implements SqlTypeDescriptor {
return true; return true;
} }
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( BigDecimal.class );
}
@Override
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaTypeDescriptor<T> javaTypeDescriptor) {
//noinspection unchecked
return new JdbcLiteralFormatterNumericData( javaTypeDescriptor, BigDecimal.class );
}
@Override @Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) { return new BasicBinder<X>( javaTypeDescriptor, this ) {

View File

@ -15,7 +15,10 @@ import java.sql.Types;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.internal.JdbcLiteralFormatterNumericData;
import org.hibernate.type.spi.TypeConfiguration;
/** /**
* Descriptor for {@link Types#DOUBLE DOUBLE} handling. * Descriptor for {@link Types#DOUBLE DOUBLE} handling.
@ -38,6 +41,17 @@ public class DoubleTypeDescriptor implements SqlTypeDescriptor {
return true; return true;
} }
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Double.class );
}
@Override
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaTypeDescriptor<T> javaTypeDescriptor) {
//noinspection unchecked
return new JdbcLiteralFormatterNumericData( javaTypeDescriptor, Double.class );
}
@Override @Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) { return new BasicBinder<X>( javaTypeDescriptor, this ) {

View File

@ -8,6 +8,9 @@ package org.hibernate.type.descriptor.sql;
import java.sql.Types; import java.sql.Types;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/** /**
* Descriptor for {@link Types#FLOAT FLOAT} handling. * Descriptor for {@link Types#FLOAT FLOAT} handling.
* *
@ -19,6 +22,11 @@ public class FloatTypeDescriptor extends RealTypeDescriptor {
public FloatTypeDescriptor() { public FloatTypeDescriptor() {
} }
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Double.class );
}
@Override @Override
public int getSqlType() { public int getSqlType() {
return Types.FLOAT; return Types.FLOAT;

View File

@ -15,7 +15,10 @@ import java.sql.Types;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.internal.JdbcLiteralFormatterNumericData;
import org.hibernate.type.spi.TypeConfiguration;
/** /**
* Descriptor for {@link Types#INTEGER INTEGER} handling. * Descriptor for {@link Types#INTEGER INTEGER} handling.
@ -38,6 +41,17 @@ public class IntegerTypeDescriptor implements SqlTypeDescriptor {
return true; return true;
} }
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Integer.class );
}
@Override
@SuppressWarnings("unchecked")
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaTypeDescriptor<T> javaTypeDescriptor) {
return new JdbcLiteralFormatterNumericData( javaTypeDescriptor, Integer.class );
}
@Override @Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) { return new BasicBinder<X>( javaTypeDescriptor, this ) {

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later * 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 * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
package org.hibernate.sql.ast.spi; package org.hibernate.type.descriptor.sql;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;

View File

@ -12,13 +12,15 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import org.hibernate.sql.ast.spi.JdbcLiteralFormatter;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.internal.JdbcLiteralFormatterCharacterData;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.sql.ast.spi.JdbcLiteralFormatter.NULL; import static org.hibernate.type.descriptor.sql.JdbcLiteralFormatter.NULL;
/** /**
* Descriptor for {@link Types#NVARCHAR NVARCHAR} handling. * Descriptor for {@link Types#NVARCHAR NVARCHAR} handling.
@ -41,9 +43,15 @@ public class NVarcharTypeDescriptor implements SqlTypeDescriptor {
return true; return true;
} }
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( String.class );
}
@Override @Override
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaTypeDescriptor<T> javaTypeDescriptor) { public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaTypeDescriptor<T> javaTypeDescriptor) {
return (value, dialect, session) -> value == null ? NULL : "'" + value.toString() + "'"; //noinspection unchecked
return new JdbcLiteralFormatterCharacterData( javaTypeDescriptor, true );
} }
@Override @Override

View File

@ -15,7 +15,10 @@ import java.sql.Types;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.internal.JdbcLiteralFormatterNumericData;
import org.hibernate.type.spi.TypeConfiguration;
/** /**
* Descriptor for {@link Types#REAL REAL} handling. * Descriptor for {@link Types#REAL REAL} handling.
@ -38,6 +41,17 @@ public class RealTypeDescriptor implements SqlTypeDescriptor {
return true; return true;
} }
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Float.class );
}
@Override
@SuppressWarnings("unchecked")
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaTypeDescriptor<T> javaTypeDescriptor) {
return new JdbcLiteralFormatterNumericData( javaTypeDescriptor, Float.class );
}
@Override @Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) { return new BasicBinder<X>( javaTypeDescriptor, this ) {

View File

@ -15,7 +15,10 @@ import java.sql.Types;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.internal.JdbcLiteralFormatterNumericData;
import org.hibernate.type.spi.TypeConfiguration;
/** /**
* Descriptor for {@link Types#SMALLINT SMALLINT} handling. * Descriptor for {@link Types#SMALLINT SMALLINT} handling.
@ -38,6 +41,17 @@ public class SmallIntTypeDescriptor implements SqlTypeDescriptor {
return true; return true;
} }
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Short.class );
}
@Override
@SuppressWarnings("unchecked")
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaTypeDescriptor<T> javaTypeDescriptor) {
return new JdbcLiteralFormatterNumericData( javaTypeDescriptor, Short.class );
}
@Override @Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) { return new BasicBinder<X>( javaTypeDescriptor, this ) {

View File

@ -8,7 +8,6 @@ package org.hibernate.type.descriptor.sql;
import java.io.Serializable; import java.io.Serializable;
import org.hibernate.sql.ast.spi.JdbcLiteralFormatter;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor; import org.hibernate.type.descriptor.java.BasicJavaDescriptor;

View File

@ -14,10 +14,15 @@ import java.sql.Time;
import java.sql.Types; import java.sql.Types;
import java.util.Calendar; import java.util.Calendar;
import javax.persistence.TemporalType;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.internal.JdbcLiteralFormatterTemporal;
import org.hibernate.type.spi.TypeConfiguration;
/** /**
* Descriptor for {@link Types#TIME TIME} handling. * Descriptor for {@link Types#TIME TIME} handling.
@ -40,6 +45,17 @@ public class TimeTypeDescriptor implements SqlTypeDescriptor {
return true; return true;
} }
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Time.class );
}
@Override
@SuppressWarnings("unchecked")
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaTypeDescriptor<T> javaTypeDescriptor) {
return new JdbcLiteralFormatterTemporal( javaTypeDescriptor, TemporalType.TIME );
}
@Override @Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) { return new BasicBinder<X>( javaTypeDescriptor, this ) {

View File

@ -14,10 +14,15 @@ import java.sql.Timestamp;
import java.sql.Types; import java.sql.Types;
import java.util.Calendar; import java.util.Calendar;
import javax.persistence.TemporalType;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.internal.JdbcLiteralFormatterTemporal;
import org.hibernate.type.spi.TypeConfiguration;
/** /**
* Descriptor for {@link Types#TIMESTAMP TIMESTAMP} handling. * Descriptor for {@link Types#TIMESTAMP TIMESTAMP} handling.
@ -40,6 +45,17 @@ public class TimestampTypeDescriptor implements SqlTypeDescriptor {
return true; return true;
} }
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Timestamp.class );
}
@Override
@SuppressWarnings("unchecked")
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaTypeDescriptor<T> javaTypeDescriptor) {
return new JdbcLiteralFormatterTemporal( javaTypeDescriptor, TemporalType.TIMESTAMP );
}
@Override @Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) { return new BasicBinder<X>( javaTypeDescriptor, this ) {

View File

@ -15,7 +15,10 @@ import java.sql.Types;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.internal.JdbcLiteralFormatterNumericData;
import org.hibernate.type.spi.TypeConfiguration;
/** /**
* Descriptor for {@link Types#TINYINT TINYINT} handling. * Descriptor for {@link Types#TINYINT TINYINT} handling.
@ -41,6 +44,17 @@ public class TinyIntTypeDescriptor implements SqlTypeDescriptor {
return true; return true;
} }
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Short.class );
}
@Override
@SuppressWarnings("unchecked")
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaTypeDescriptor<T> javaTypeDescriptor) {
return new JdbcLiteralFormatterNumericData( javaTypeDescriptor, Short.class );
}
@Override @Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) { return new BasicBinder<X>( javaTypeDescriptor, this ) {

View File

@ -15,7 +15,9 @@ import java.sql.Types;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/** /**
* Descriptor for {@link Types#VARBINARY VARBINARY} handling. * Descriptor for {@link Types#VARBINARY VARBINARY} handling.
@ -37,6 +39,11 @@ public class VarbinaryTypeDescriptor implements SqlTypeDescriptor {
return true; return true;
} }
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( byte[].class );
}
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) { return new BasicBinder<X>( javaTypeDescriptor, this ) {

View File

@ -12,13 +12,13 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import org.hibernate.sql.ast.spi.JdbcLiteralFormatter;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.internal.JdbcLiteralFormatterCharacterData;
import static org.hibernate.sql.ast.spi.JdbcLiteralFormatter.NULL; import org.hibernate.type.spi.TypeConfiguration;
/** /**
* Descriptor for {@link Types#VARCHAR VARCHAR} handling. * Descriptor for {@link Types#VARCHAR VARCHAR} handling.
@ -41,9 +41,15 @@ public class VarcharTypeDescriptor implements SqlTypeDescriptor {
return true; return true;
} }
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( String.class );
}
@Override @Override
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaTypeDescriptor<T> javaTypeDescriptor) { public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaTypeDescriptor<T> javaTypeDescriptor) {
return (value, dialect, session) -> value == null ? NULL : "'" + value.toString() + "'"; //noinspection unchecked
return new JdbcLiteralFormatterCharacterData( javaTypeDescriptor );
} }
@Override @Override

View File

@ -0,0 +1,28 @@
/*
* 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.type.descriptor.sql.internal;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.spi.BasicJdbcLiteralFormatter;
/**
* JdbcLiteralFormatter implementation for handling boolean literals
*
* @author Steve Ebersole
*/
public class JdbcLiteralFormatterBoolean extends BasicJdbcLiteralFormatter {
public JdbcLiteralFormatterBoolean(JavaTypeDescriptor javaTypeDescriptor) {
super( javaTypeDescriptor );
}
@Override
public String toJdbcLiteral(Object value, Dialect dialect, SharedSessionContractImplementor session) {
return unwrap( value, Boolean.class, session ).toString();
}
}

View File

@ -0,0 +1,45 @@
/*
* 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.type.descriptor.sql.internal;
import java.util.Locale;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.spi.BasicJdbcLiteralFormatter;
/**
* JdbcLiteralFormatter implementation for handling character data
*
* @author Steve Ebersole
*/
public class JdbcLiteralFormatterCharacterData extends BasicJdbcLiteralFormatter {
private final boolean isNationalized;
public JdbcLiteralFormatterCharacterData(JavaTypeDescriptor javaTypeDescriptor) {
this( javaTypeDescriptor, false );
}
public JdbcLiteralFormatterCharacterData(JavaTypeDescriptor javaTypeDescriptor, boolean isNationalized) {
super( javaTypeDescriptor );
this.isNationalized = isNationalized;
}
@Override
public String toJdbcLiteral(Object value, Dialect dialect, SharedSessionContractImplementor session) {
final String literalValue = unwrap( value, String.class, session );
if ( isNationalized ) {
// is there a standardized form for n-string literals? This is the SQL Server syntax for sure
return String.format( Locale.ROOT, "n'%s'", literalValue );
}
else {
return String.format( Locale.ROOT, "'%s'", literalValue );
}
}
}

View File

@ -0,0 +1,31 @@
/*
* 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.type.descriptor.sql.internal;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.spi.BasicJdbcLiteralFormatter;
/**
* @author Steve Ebersole
*/
public class JdbcLiteralFormatterNumericData extends BasicJdbcLiteralFormatter {
private final Class<? extends Number> unwrapJavaType;
public JdbcLiteralFormatterNumericData(
JavaTypeDescriptor javaTypeDescriptor,
Class<? extends Number> unwrapJavaType) {
super( javaTypeDescriptor );
this.unwrapJavaType = unwrapJavaType;
}
@Override
public String toJdbcLiteral(Object value, Dialect dialect, SharedSessionContractImplementor session) {
return unwrap( value, unwrapJavaType, session ).toString();
}
}

View File

@ -0,0 +1,71 @@
/*
* 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.type.descriptor.sql.internal;
import java.time.temporal.TemporalAccessor;
import javax.persistence.TemporalType;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.spi.BasicJdbcLiteralFormatter;
/**
* @author Steve Ebersole
*/
public class JdbcLiteralFormatterTemporal extends BasicJdbcLiteralFormatter {
private final TemporalType precision;
public JdbcLiteralFormatterTemporal(JavaTypeDescriptor javaTypeDescriptor, TemporalType precision) {
super( javaTypeDescriptor );
this.precision = precision;
}
@Override
public String toJdbcLiteral(Object value, Dialect dialect, SharedSessionContractImplementor session) {
// for performance reasons, avoid conversions if we can
if ( value instanceof java.util.Date ) {
return dialect.formatDateTimeLiteral(
(java.util.Date) value,
precision
);
}
else if ( value instanceof java.util.Calendar ) {
return dialect.formatDateTimeLiteral(
(java.util.Calendar) value,
precision
);
}
else if ( value instanceof TemporalAccessor ) {
return dialect.formatDateTimeLiteral(
(TemporalAccessor) value,
precision
);
}
switch ( precision) {
case DATE: {
return dialect.formatDateTimeLiteral(
unwrap( value, java.sql.Date.class, session ),
precision
);
}
case TIME: {
return dialect.formatDateTimeLiteral(
unwrap( value, java.sql.Time.class, session ),
precision
);
}
default: {
return dialect.formatDateTimeLiteral(
unwrap( value, java.util.Date.class, session ),
precision
);
}
}
}
}

View File

@ -0,0 +1,27 @@
/*
* 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.type.descriptor.sql.spi;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.JdbcLiteralFormatter;
/**
* Abstract JdbcLiteralFormatter implementation managing the JavaTypeDescriptor
*
* @author Steve Ebersole
*/
public abstract class AbstractJdbcLiteralFormatter implements JdbcLiteralFormatter {
private final JavaTypeDescriptor javaTypeDescriptor;
public AbstractJdbcLiteralFormatter(JavaTypeDescriptor javaTypeDescriptor) {
this.javaTypeDescriptor = javaTypeDescriptor;
}
protected JavaTypeDescriptor getJavaTypeDescriptor() {
return javaTypeDescriptor;
}
}

View File

@ -0,0 +1,34 @@
/*
* 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.type.descriptor.sql.spi;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
/**
* Support for JdbcLiteralFormatter implementations with a basic implementation of an unwrap method
*
* @author Steve Ebersole
*/
public abstract class BasicJdbcLiteralFormatter extends AbstractJdbcLiteralFormatter {
public BasicJdbcLiteralFormatter(JavaTypeDescriptor<?> javaTypeDescriptor) {
super( javaTypeDescriptor );
}
@SuppressWarnings("unchecked")
protected <X> X unwrap(Object value, Class<X> unwrapType, SharedSessionContractImplementor session) {
assert value != null;
// for performance reasons, avoid conversions if we can
if ( unwrapType.isInstance( value ) ) {
return (X) value;
}
return (X) getJavaTypeDescriptor().unwrap( value, unwrapType, session );
}
}

View File

@ -6,35 +6,92 @@
*/ */
package org.hibernate.orm.test.query.hql; package org.hibernate.orm.test.query.hql;
import org.hibernate.boot.MetadataSources;
import org.hibernate.orm.test.query.sqm.BaseSqmUnitTest;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.testing.orm.domain.StandardDomainModel; import org.hibernate.testing.orm.domain.StandardDomainModel;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class LiteralTests extends BaseSqmUnitTest { @SuppressWarnings("WeakerAccess")
@ServiceRegistry
@DomainModel( standardModels = StandardDomainModel.GAMBIT )
@SessionFactory
public class LiteralTests {
@Override @Test
protected void applyMetadataSources(MetadataSources metadataSources) { public void testJdbcTimeLiteral(SessionFactoryScope scope) {
StandardDomainModel.GAMBIT.getDescriptor().applyDomainModel( metadataSources ); scope.inTransaction(
session -> {
session.createQuery( "from EntityOfBasics e1 where e1.theTime = {t 12:30:00}" ).list();
session.createQuery( "from EntityOfBasics e1 where e1.theTime = {t '12:30:00'}" ).list();
}
);
} }
@Test @Test
public void testTimestampLiteral() { public void testJdbcDateLiteral(SessionFactoryScope scope) {
final SqmSelectStatement sqm = interpretSelect( "from EntityOfBasics e1 where e1.theTimestamp = {ts '2018-01-01T12:30:00'}" ); scope.inTransaction(
session -> {
session.createQuery( "from EntityOfBasics e1 where e1.theDate = {d 1999-12-31}" ).list();
session.createQuery( "from EntityOfBasics e1 where e1.theDate = {d '1999-12-31'}" ).list();
}
);
} }
@Test @Test
public void testDateLiteral() { public void testJdbcTimestampLiteral(SessionFactoryScope scope) {
final SqmSelectStatement sqm = interpretSelect( "from EntityOfBasics e1 where e1.theDate = {d '2018-01-01'}" ); scope.inTransaction(
session -> {
session.createQuery( "from EntityOfBasics e1 where e1.theDate = {ts 1999-12-31 12:30:00}" ).list();
session.createQuery( "from EntityOfBasics e1 where e1.theDate = {ts '1999-12-31 12:30:00'}" ).list();
}
);
} }
@Test @Test
public void testTimeLiteral() { public void testLocalDateLiteral(SessionFactoryScope scope) {
final SqmSelectStatement sqm = interpretSelect( "from EntityOfBasics e1 where e1.theTime = {t '12:30:00'}" ); scope.inTransaction(
session -> {
session.createQuery( "from EntityOfBasics e1 where e1.theLocalDate = {1999-12-31}" ).list();
}
);
}
@Test
public void testLocalTimeLiteral(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "from EntityOfBasics e1 where e1.theLocalTime = {12:59:59}" ).list();
}
);
}
@Test
public void testDateTimeLiteral(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
// todo (6.0) : removed this difference between the string-literal form and the date-time-field form (with/without 'T')
session.createQuery( "from EntityOfBasics e1 where e1.theLocalDateTime = {1999-12-31 12:59:59}" ).list();
session.createQuery( "from EntityOfBasics e1 where e1.theZonedDateTime = {1999-12-31 12:59:59 +01:00}" ).list();
session.createQuery( "from EntityOfBasics e1 where e1.theZonedDateTime = {1999-12-31 12:59:59 CST}" ).list();
}
);
}
@Test
public void isolated(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "from EntityOfBasics e1 where e1.theLocalDateTime = {1999-12-31 12:59:59}" ).list();
session.createQuery( "from EntityOfBasics e1 where e1.theZonedDateTime = {1999-12-31 12:59:59 +01:00}" ).list();
}
);
} }
} }

View File

@ -140,7 +140,6 @@ public class StandardFunctionTests {
} }
@Test @Test
@FailureExpected
public void testCoalesceFunction(SessionFactoryScope scope) { public void testCoalesceFunction(SessionFactoryScope scope) {
scope.inTransaction( scope.inTransaction(
session -> { session -> {
@ -617,120 +616,176 @@ public class StandardFunctionTests {
public void isolated(SessionFactoryScope scope) { public void isolated(SessionFactoryScope scope) {
scope.inTransaction( scope.inTransaction(
session -> { session -> {
session.createQuery("select extract(time from local_datetime), extract(date from local_datetime) from EntityOfBasics e") session.createQuery("select extract(time from e.theTimestamp), extract(date from e.theTimestamp) from EntityOfBasics e").list();
.list(); session.createQuery("select extract(time from local_datetime), extract(date from local_datetime) from EntityOfBasics e").list();
} }
); );
} }
@Test @Test
@FailureExpected // @FailureExpected
public void testExtractFunctionWithAssertions(SessionFactoryScope scope) { public void testExtractFunctionWithAssertions(SessionFactoryScope scope) {
scope.inTransaction( scope.inTransaction(
session -> { session -> {
EntityOfBasics entity = new EntityOfBasics(); EntityOfBasics entity = new EntityOfBasics();
entity.setId(1); entity.setId(1);
session.save(entity); session.save(entity);
session.flush(); }
);
try {
scope.inTransaction(
session -> {
assertThat( assertThat(
session.createQuery("select extract(week of year from date '2019-01-01') from EntityOfBasics").getResultList().get(0), session.createQuery(
"select extract(week of year from {2019-01-01}) from EntityOfBasics b where b.id = 1" )
.getResultList()
.get( 0 ),
is( 1 ) is( 1 )
); );
assertThat( assertThat(
session.createQuery("select extract(week of year from date '2019-01-05') from EntityOfBasics").getResultList().get(0), session.createQuery(
"select extract(week of year from {2019-01-01}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 1 ) is( 1 )
); );
assertThat( assertThat(
session.createQuery("select extract(week of year from date '2019-01-06') from EntityOfBasics").getResultList().get(0), session.createQuery(
is(2) "select extract(week of year from {2019-01-01}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 1 )
);
assertThat(
session.createQuery(
"select extract(week of year from {2019-01-01}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 1 )
); );
assertThat( assertThat(
session.createQuery("select extract(week of month from date '2019-05-01') from EntityOfBasics").getResultList().get(0), session.createQuery(
"select extract(week of year from {2019-01-05}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 1 ) is( 1 )
); );
assertThat(
session.createQuery("select extract(week of month from date '2019-05-04') from EntityOfBasics").getResultList().get(0),
is(1)
);
assertThat(
session.createQuery("select extract(week of month from date '2019-05-05') from EntityOfBasics").getResultList().get(0),
is(2)
);
assertThat( assertThat(
session.createQuery("select extract(week from date '2019-05-27') from EntityOfBasics").getResultList().get(0), session.createQuery(
"select extract(week of month from {2019-05-01}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 1 )
);
assertThat(
session.createQuery( "select extract(week from {2019-05-27}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 22 ) is( 22 )
); );
assertThat(
session.createQuery("select extract(week from date '2019-06-02') from EntityOfBasics").getResultList().get(0),
is(22)
);
assertThat(
session.createQuery("select extract(week from date '2019-06-03') from EntityOfBasics").getResultList().get(0),
is(23)
);
assertThat( assertThat(
session.createQuery("select extract(day of year from date '2019-05-30') from EntityOfBasics").getResultList().get(0), session.createQuery(
"select extract(day of year from {2019-05-30}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 150 ) is( 150 )
); );
assertThat( assertThat(
session.createQuery("select extract(day of month from date '2019-05-27') from EntityOfBasics").getResultList().get(0), session.createQuery(
"select extract(day of month from {2019-05-27}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 27 ) is( 27 )
); );
assertThat( assertThat(
session.createQuery("select extract(day from date '2019-05-31') from EntityOfBasics").getResultList().get(0), session.createQuery( "select extract(day from {2019-05-31}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 31 ) is( 31 )
); );
assertThat( assertThat(
session.createQuery("select extract(month from date '2019-05-31') from EntityOfBasics").getResultList().get(0), session.createQuery( "select extract(month from {2019-05-31}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 5 ) is( 5 )
); );
assertThat( assertThat(
session.createQuery("select extract(year from date '2019-05-31') from EntityOfBasics").getResultList().get(0), session.createQuery( "select extract(year from {2019-05-31}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 2019 ) is( 2019 )
); );
assertThat( assertThat(
session.createQuery("select extract(quarter from date '2019-05-31') from EntityOfBasics").getResultList().get(0), session.createQuery(
"select extract(quarter from {2019-05-31}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 2 ) is( 2 )
); );
assertThat( assertThat(
session.createQuery("select extract(day of week from date '2019-05-27') from EntityOfBasics").getResultList().get(0), session.createQuery(
"select extract(day of week from {2019-05-27}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 2 ) is( 2 )
); );
assertThat( assertThat(
session.createQuery("select extract(day of week from date '2019-05-31') from EntityOfBasics").getResultList().get(0), session.createQuery(
"select extract(day of week from {2019-05-31}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 6 ) is( 6 )
); );
assertThat( assertThat(
session.createQuery("select extract(second from time '14:12:10') from EntityOfBasics").getResultList().get(0), session.createQuery( "select extract(second from {14:12:10}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 10f ) is( 10f )
); );
assertThat( assertThat(
session.createQuery("select extract(minute from time '14:12:10') from EntityOfBasics").getResultList().get(0), session.createQuery( "select extract(minute from {14:12:10}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 12 ) is( 12 )
); );
assertThat( assertThat(
session.createQuery("select extract(hour from time '14:12:10') from EntityOfBasics").getResultList().get(0), session.createQuery( "select extract(hour from {14:12:10}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 14 ) is( 14 )
); );
assertThat( assertThat(
session.createQuery("select extract(date from current datetime) from EntityOfBasics").getResultList().get(0), session.createQuery( "select extract(date from local_datetime) from EntityOfBasics" )
.getResultList()
.get( 0 ),
instanceOf( LocalDate.class ) instanceOf( LocalDate.class )
); );
assertThat( assertThat(
session.createQuery("select extract(time from current datetime) from EntityOfBasics").getResultList().get(0), session.createQuery( "select extract(time from local_datetime) from EntityOfBasics" )
.getResultList()
.get( 0 ),
instanceOf( LocalTime.class ) instanceOf( LocalTime.class )
); );
session.delete(entity);
} }
); );
} }
finally {
scope.inTransaction(
session -> {
session.createQuery( "delete from EntityOfBasics" ).executeUpdate();
}
);
}
}
@Test @Test
public void testExtractFunctions(SessionFactoryScope scope) { public void testExtractFunctions(SessionFactoryScope scope) {
@ -795,14 +850,8 @@ public class StandardFunctionTests {
public void testCountFunction(SessionFactoryScope scope) { public void testCountFunction(SessionFactoryScope scope) {
scope.inTransaction( scope.inTransaction(
session -> { session -> {
session.createQuery("select count(*) from EntityOfBasics e") session.createQuery("select count(e) from EntityOfBasics e").list();
.list(); session.createQuery("select count(distinct e) from EntityOfBasics e").list();
session.createQuery("select count(1) from EntityOfBasics e")
.list();
session.createQuery("select count(e) from EntityOfBasics e")
.list();
session.createQuery("select count(distinct e) from EntityOfBasics e")
.list();
} }
); );
} }
@ -820,7 +869,6 @@ public class StandardFunctionTests {
} }
@Test @Test
@FailureExpected
public void testFormat(SessionFactoryScope scope) { public void testFormat(SessionFactoryScope scope) {
scope.inTransaction( scope.inTransaction(
session -> { session -> {
@ -830,24 +878,44 @@ public class StandardFunctionTests {
entity.setTheTime( new Time( 23, 10, 8 ) ); entity.setTheTime( new Time( 23, 10, 8 ) );
entity.setTheTimestamp( new Timestamp( System.currentTimeMillis() ) ); entity.setTheTimestamp( new Timestamp( System.currentTimeMillis() ) );
session.persist( entity ); session.persist( entity );
session.flush(); }
);
try {
scope.inTransaction(
session -> {
session.createQuery( "select format(e.theTime as 'hh:mm:ss aa') from EntityOfBasics e" ) session.createQuery( "select format(e.theTime as 'hh:mm:ss aa') from EntityOfBasics e" )
.list(); .list();
session.createQuery("select format(e.theDate as 'dd/MM/yy'), format(e.theDate as 'EEEE, MMMM dd, yyyy') from EntityOfBasics e") session.createQuery(
"select format(e.theDate as 'dd/MM/yy'), format(e.theDate as 'EEEE, MMMM dd, yyyy') from EntityOfBasics e" )
.list(); .list();
session.createQuery("select format(e.theTimestamp as 'dd/MM/yyyy ''at'' HH:mm:ss') from EntityOfBasics e") session.createQuery(
"select format(e.theTimestamp as 'dd/MM/yyyy ''at'' HH:mm:ss') from EntityOfBasics e" )
.list(); .list();
assertThat( assertThat(
session.createQuery("select format(e.theDate as 'EEEE, dd/MM/yyyy') from EntityOfBasics").getResultList().get(0), session.createQuery(
"select format(e.theDate as 'EEEE, dd/MM/yyyy') from EntityOfBasics e" )
.getResultList()
.get( 0 ),
is( "Monday, 25/03/1974" ) is( "Monday, 25/03/1974" )
); );
assertThat( assertThat(
session.createQuery("select format(e.theTime as '''Hello'', hh:mm:ss aa') from EntityOfBasics").getResultList().get(0), session.createQuery(
"select format(e.theTime as '''Hello'', hh:mm:ss aa') from EntityOfBasics e" )
.getResultList()
.get( 0 ),
is( "Hello, 11:10:08 PM" ) is( "Hello, 11:10:08 PM" )
); );
} }
); );
} }
finally {
scope.inTransaction(
session -> {
session.createQuery( "delete EntityOfBasics" ).executeUpdate();
}
);
}
}
} }

View File

@ -9,6 +9,11 @@ package org.hibernate.testing.orm.domain.gambit;
import java.net.URL; import java.net.URL;
import java.sql.Clob; import java.sql.Clob;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;
import java.util.Date; import java.util.Date;
import javax.persistence.AttributeConverter; import javax.persistence.AttributeConverter;
import javax.persistence.Column; import javax.persistence.Column;
@ -42,6 +47,11 @@ public class EntityOfBasics {
private Date theTime; private Date theTime;
private Date theTimestamp; private Date theTimestamp;
private Instant theInstant; private Instant theInstant;
private LocalDateTime theLocalDateTime;
private LocalDate theLocalDate;
private LocalTime theLocalTime;
private OffsetDateTime theOffsetDateTime;
private ZonedDateTime theZonedDateTime;
private Gender gender; private Gender gender;
private Gender convertedGender; private Gender convertedGender;
private Gender ordinalGender; private Gender ordinalGender;
@ -167,6 +177,46 @@ public class EntityOfBasics {
this.theInstant = theInstant; this.theInstant = theInstant;
} }
public LocalDateTime getTheLocalDateTime() {
return theLocalDateTime;
}
public void setTheLocalDateTime(LocalDateTime theLocalDateTime) {
this.theLocalDateTime = theLocalDateTime;
}
public LocalDate getTheLocalDate() {
return theLocalDate;
}
public void setTheLocalDate(LocalDate theLocalDate) {
this.theLocalDate = theLocalDate;
}
public LocalTime getTheLocalTime() {
return theLocalTime;
}
public void setTheLocalTime(LocalTime theLocalTime) {
this.theLocalTime = theLocalTime;
}
public OffsetDateTime getTheOffsetDateTime() {
return theOffsetDateTime;
}
public void setTheOffsetDateTime(OffsetDateTime theOffsetDateTime) {
this.theOffsetDateTime = theOffsetDateTime;
}
public ZonedDateTime getTheZonedDateTime() {
return theZonedDateTime;
}
public void setTheZonedDateTime(ZonedDateTime theZonedDateTime) {
this.theZonedDateTime = theZonedDateTime;
}
public static class GenderConverter implements AttributeConverter<Gender,Character> { public static class GenderConverter implements AttributeConverter<Gender,Character> {
@Override @Override
public Character convertToDatabaseColumn(Gender attribute) { public Character convertToDatabaseColumn(Gender attribute) {