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'
junit5Version = '5.3.1'
h2Version = '1.4.196'
h2Version = '1.4.199'
bytemanVersion = '4.0.8' //Compatible with JDK14
jnpVersion = '5.0.6.CR1'

View File

@ -11,60 +11,91 @@ lexer grammar HqlLexer;
package org.hibernate.grammars.hql;
}
WS : ( ' ' | '\t' | '\f' | EOL ) -> skip;
WS : WS_CHAR+ -> skip;
fragment
EOL : [\r\n]+;
INTEGER_LITERAL : INTEGER_NUMBER ;
WS_CHAR : [ \f\t\r\n];
fragment
INTEGER_NUMBER : ('0' | '1'..'9' '0'..'9'*) ;
LONG_LITERAL : INTEGER_NUMBER ('l'|'L');
BIG_INTEGER_LITERAL : INTEGER_NUMBER ('bi'|'BI') ;
HEX_LITERAL : '0' ('x'|'X') HEX_DIGIT+ ('l'|'L')? ;
DIGIT : [0-9];
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
FLOATING_POINT_NUMBER
: ('0'..'9')+ '.' ('0'..'9')* EXPONENT?
| '.' ('0'..'9')+ EXPONENT?
| ('0'..'9')+ EXPONENT
| ('0'..'9')+
: DIGIT+ '.' DIGIT* EXPONENT?
| '.' DIGIT+ EXPONENT?
| DIGIT+ EXPONENT
| DIGIT+
;
DOUBLE_LITERAL : FLOATING_POINT_NUMBER ('d'|'D') ;
BIG_DECIMAL_LITERAL : FLOATING_POINT_NUMBER ('bd'|'BD') ;
fragment
EXPONENT : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;
fragment SINGLE_QUOTE : '\'';
fragment DOUBLE_QUOTE : '"';
CHARACTER_LITERAL
: '\'' ( 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 BACKSLASH : '\\';
fragment
ESCAPE_SEQUENCE
: '\\' ('b'|'t'|'n'|'f'|'r'|'\\"'|'\''|'\\')
| UNICODE_ESCAPE
| OCTAL_ESCAPE
: BACKSLASH [btnfr"']
| BACKSLASH UNICODE_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
OCTAL_ESCAPE
: '\\' ('0'..'3') ('0'..'7') ('0'..'7')
@ -72,10 +103,6 @@ OCTAL_ESCAPE
| '\\' ('0'..'7')
;
fragment
UNICODE_ESCAPE
: '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
;
// ESCAPE start tokens
TIMESTAMP_ESCAPE_START : '{ts';
@ -134,6 +161,7 @@ CONCAT : [cC] [oO] [nN] [cC] [aA] [tT];
COUNT : [cC] [oO] [uU] [nN] [tT];
CROSS : [cC] [rR] [oO] [sS] [sS];
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_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];
@ -155,6 +183,7 @@ FETCH : [fF] [eE] [tT] [cC] [hH];
FLOOR : [fF] [lL] [oO] [oO] [rR];
FROM : [fF] [rR] [oO] [mM];
FOR : [fF] [oO] [rR];
FORMAT : [fF] [oO] [rR] [mM] [aA] [tT];
FULL : [fF] [uU] [lL] [lL];
FUNCTION : [fF] [uU] [nN] [cC] [tT] [iI] [oO] [nN];
GREATEST : [gG] [rR] [eE] [aA] [tT] [eE] [sS] [tT];

View File

@ -466,7 +466,6 @@ nullIf
literal
: STRING_LITERAL
| CHARACTER_LITERAL
| INTEGER_LITERAL
| LONG_LITERAL
| BIG_INTEGER_LITERAL
@ -478,40 +477,79 @@ literal
| NULL
| TRUE
| FALSE
| timestampLiteral
| dateLiteral
| timeLiteral
| temporalLiteral
| generalizedLiteral
;
// todo (6.0) : expand temporal literal support to Java 8 temporal types
// * Instant -> {instant '...'}
// * LocalDate -> {localDate '...'}
// * LocalDateTime -> {localDateTime '...'}
// * OffsetDateTime -> {offsetDateTime '...'}
// * OffsetTime -> {offsetTime '...'}
// * ZonedDateTime -> {localDate '...'}
// * ...
//
// 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
temporalLiteral
: dateTimeLiteral
| dateLiteral
| timeLiteral
| jdbcTimestampLiteral
| jdbcDateLiteral
| jdbcTimeLiteral
;
timestampLiteral
: TIMESTAMP_ESCAPE_START dateTimeLiteralText RIGHT_BRACE
dateTimeLiteral
: LEFT_BRACE dateTime RIGHT_BRACE
;
dateLiteral
: DATE_ESCAPE_START dateTimeLiteralText RIGHT_BRACE
: LEFT_BRACE date RIGHT_BRACE
;
timeLiteral
: TIME_ESCAPE_START dateTimeLiteralText RIGHT_BRACE
: LEFT_BRACE time RIGHT_BRACE
;
dateTimeLiteralText
: STRING_LITERAL | CHARACTER_LITERAL
dateTime
: 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
: COLON identifier # NamedParameter
| QUESTION_MARK INTEGER_LITERAL? # PositionalParameter
@ -589,6 +627,7 @@ countFunction
standardFunction
: castFunction
| extractFunction
| formatFunction
| concatFunction
| substringFunction
| replaceFunction
@ -666,7 +705,7 @@ trimSpecification
;
trimCharacter
: CHARACTER_LITERAL | STRING_LITERAL
: STRING_LITERAL
;
upperFunction

View File

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

View File

@ -15,6 +15,7 @@ import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.H2ExtractEmulation;
import org.hibernate.dialect.function.Replacer;
import org.hibernate.dialect.hint.IndexQueryHintHandler;
import org.hibernate.dialect.identity.H2IdentityColumnSupport;
import org.hibernate.dialect.identity.IdentityColumnSupport;
@ -205,6 +206,19 @@ public class H2Dialect extends Dialect {
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
public String getAddColumnString() {
return "add column";

View File

@ -13,6 +13,7 @@ import java.util.Locale;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.InformixExtractEmulation;
import org.hibernate.dialect.function.Replacer;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.InformixIdentityColumnSupport;
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
public String getAddColumnString() {
return "add";

View File

@ -188,6 +188,22 @@ public class IngresDialect extends Dialect {
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
public String getSelectGUIDString() {
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.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.MySQLExtractEmulation;
import org.hibernate.dialect.function.Replacer;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.MySQLIdentityColumnSupport;
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() {
// 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.NvlCoalesceEmulation;
import org.hibernate.dialect.function.OracleExtractEmulation;
import org.hibernate.dialect.function.Replacer;
import org.hibernate.dialect.pagination.AbstractLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler;
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.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.PostgresExtractEmulation;
import org.hibernate.dialect.function.Replacer;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.PostgreSQL81IdentityColumnSupport;
import org.hibernate.dialect.pagination.AbstractLimitHandler;

View File

@ -12,12 +12,14 @@ import java.util.Locale;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.Replacer;
import org.hibernate.dialect.function.TransactSQLTrimEmulation;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.SQLServerIdentityColumnSupport;
import org.hibernate.dialect.pagination.LegacyLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.TopLimitHandler;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.type.descriptor.sql.SmallIntTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
@ -59,6 +61,58 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
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
public String getNoColumnsInsertString() {
return "default values";

View File

@ -7,7 +7,6 @@
package org.hibernate.dialect.function;
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.StandardFunctionReturnTypeResolvers;
import org.hibernate.type.StandardBasicTypes;

View File

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

View File

@ -157,7 +157,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
private final boolean[] isNullableTable;
private final boolean[] isInverseTable;
private final Map<String, String> discriminatorValuesByTableName;
private final Map<String, Object> discriminatorValuesByTableName;
private final Map<String, String> subclassNameByTableName;
//INITIALIZATION:
@ -532,7 +532,22 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
discriminatorValuesByTableName = new LinkedHashMap<>( 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[subclassSpan - 1] = discriminatorSQLString;
notNullColumnTableNumbers = new int[subclassSpan];
@ -590,7 +605,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
// "foo.class = Bar" works in HQL
discriminatorValue = sc.getSubclassId();
}
discriminatorValuesByTableName.put( sc.getTable().getName(), discriminatorValue.toString() );
discriminatorValuesByTableName.put( sc.getTable().getName(), discriminatorValue );
subclassesByDiscriminatorValue.put( discriminatorValue, sc.getEntityName() );
discriminatorValues[k] = discriminatorValue.toString();
int id = getTableId(
@ -1320,11 +1335,10 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
ColumnReference identifierColumnReference,
BasicType resultType) {
final Predicate predicate = new NullnessPredicate( identifierColumnReference, true );
final Expression expression =
new QueryLiteral<>(
discriminatorValuesByTableName.get( table.getTableExpression() ),
resultType
);
final Expression expression = new QueryLiteral<>(
discriminatorValuesByTableName.get( table.getTableExpression() ),
resultType
);
caseSearchedExpression.when( predicate, expression );
}

View File

@ -103,7 +103,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
private final int[] subclassFormulaTableNumberClosure;
// 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 String discriminatorColumnName;
private final String discriminatorColumnReaders;

View File

@ -36,6 +36,9 @@ public enum QueryLiteralRendering {
/**
* As a parameter when the literal occurs outside the SELECT clause,
* 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" );

View File

@ -12,13 +12,19 @@ import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import org.hibernate.NotYetImplementedFor6Exception;
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.StandardStack;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.mapping.MappingModelExpressable;
import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType;
import org.hibernate.metamodel.model.domain.BasicDomainType;
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.spi.ParameterDeclarationContext;
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.SqmStatement;
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.SqmPolymorphicRootDescriptor;
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.SqmCaseSearched;
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.SqmExpression;
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.SqmLiteral;
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.SqmPathEntityType;
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.SqmTrimSpecification;
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.SqmSubQuery;
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.DateTimeUtils;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
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_YEAR;
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.TIMEZONE_HOUR;
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
* query.
*/
@SuppressWarnings("WeakerAccess")
public static SqmStatement buildSemanticModel(
HqlParser.StatementContext hqlParseTree,
SqmCreationOptions creationOptions,
@ -195,6 +208,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
return processingStateStack;
}
@SuppressWarnings("WeakerAccess")
public SemanticQueryBuilder(SqmCreationOptions creationOptions, SqmCreationContext creationContext) {
this.creationOptions = creationOptions;
this.creationContext = creationContext;
@ -1408,7 +1422,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
return new SqmFunction(
"mod",
getFunctionDescriptor( "mod" ),
(AllowableFunctionReturnType) dividend.getNodeType(),
dividend.getNodeType(),
asList( dividend, divisor ),
creationContext.getNodeBuilder()
);
@ -1513,7 +1527,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
return new SqmFunction(
"local_datetime",
getFunctionDescriptor( "local_datetime" ),
StandardBasicTypes.TIMESTAMP,
StandardBasicTypes.LOCAL_DATE_TIME,
creationContext.getNodeBuilder()
);
}
@ -1524,7 +1538,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
return new SqmFunction(
"local_date",
getFunctionDescriptor( "local_date" ),
StandardBasicTypes.TIMESTAMP,
StandardBasicTypes.LOCAL_DATE,
creationContext.getNodeBuilder()
);
}
@ -1535,7 +1549,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
return new SqmFunction(
"local_time",
getFunctionDescriptor( "local_time" ),
StandardBasicTypes.TIMESTAMP,
StandardBasicTypes.LOCAL_TIME,
creationContext.getNodeBuilder()
);
}
@ -1568,7 +1582,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
return new SqmFunction(
"least",
getFunctionDescriptor( "least" ),
(AllowableFunctionReturnType<?>) type,
type,
arguments,
creationContext.getNodeBuilder()
);
@ -1637,22 +1651,23 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
@Override
public SqmLiteral visitLiteralExpression(HqlParser.LiteralExpressionContext ctx) {
if ( ctx.literal().CHARACTER_LITERAL() != null ) {
return characterLiteral( ctx.literal().CHARACTER_LITERAL().getText() );
}
else if ( ctx.literal().STRING_LITERAL() != null ) {
if ( ctx.literal().STRING_LITERAL() != null ) {
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() );
}
else if ( ctx.literal().LONG_LITERAL() != null ) {
if ( ctx.literal().LONG_LITERAL() != null ) {
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() );
}
else if ( ctx.literal().HEX_LITERAL() != null ) {
if ( ctx.literal().HEX_LITERAL() != null ) {
final String text = ctx.literal().HEX_LITERAL().getText();
if ( text.endsWith( "l" ) || text.endsWith( "L" ) ) {
return longLiteral( text );
@ -1661,7 +1676,8 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
return integerLiteral( text );
}
}
else if ( ctx.literal().OCTAL_LITERAL() != null ) {
if ( ctx.literal().OCTAL_LITERAL() != null ) {
final String text = ctx.literal().OCTAL_LITERAL().getText();
if ( text.endsWith( "l" ) || text.endsWith( "L" ) ) {
return longLiteral( text );
@ -1670,56 +1686,379 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
return integerLiteral( text );
}
}
else if ( ctx.literal().FLOAT_LITERAL() != null ) {
if ( ctx.literal().FLOAT_LITERAL() != null ) {
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() );
}
else if ( ctx.literal().BIG_DECIMAL_LITERAL() != null ) {
if ( ctx.literal().BIG_DECIMAL_LITERAL() != null ) {
return bigDecimalLiteral( ctx.literal().BIG_DECIMAL_LITERAL().getText() );
}
else if ( ctx.literal().FALSE() != null ) {
if ( ctx.literal().FALSE() != null ) {
return booleanLiteral( false );
}
else if ( ctx.literal().TRUE() != null ) {
if ( ctx.literal().TRUE() != null ) {
return booleanLiteral( true );
}
else if ( ctx.literal().NULL() != null ) {
if ( ctx.literal().NULL() != null ) {
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 );
}
else if ( ctx.literal().timeLiteral() != null ) {
return LiteralHelper.timeLiteralFrom( ctx.literal().timeLiteral().dateTimeLiteralText().getText(), this );
if ( ctx.literal().generalizedLiteral() != null ) {
throw new NotYetImplementedFor6Exception( getClass() );
}
// otherwise we have a problem
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) {
final SqmExpressable expressionType = resolveExpressableTypeBasic( Boolean.class );
//noinspection unchecked
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) {
return new SqmLiteral<>(
text,
@ -2208,29 +2547,29 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
@Override
public Object visitDayField(HqlParser.DayFieldContext ctx) {
NodeBuilder nodeBuilder = creationContext.getNodeBuilder();
if (ctx.WEEK()!=null) {
return new SqmExtractUnit<>(DAY_OF_WEEK, resolveExpressableTypeBasic( Integer.class ), nodeBuilder);
final NodeBuilder nodeBuilder = creationContext.getNodeBuilder();
if ( ctx.WEEK() != null ) {
return new SqmExtractUnit<>( DAY_OF_WEEK, resolveExpressableTypeBasic( Integer.class ), nodeBuilder );
}
if (ctx.MONTH()!=null) {
return new SqmExtractUnit<>(DAY_OF_MONTH, resolveExpressableTypeBasic( Integer.class ), nodeBuilder);
if ( ctx.MONTH() != null ) {
return new SqmExtractUnit<>( DAY_OF_MONTH, resolveExpressableTypeBasic( Integer.class ), nodeBuilder );
}
if (ctx.YEAR()!=null) {
return new SqmExtractUnit<>(DAY_OF_YEAR, resolveExpressableTypeBasic( Integer.class ), nodeBuilder);
if ( ctx.YEAR() != null ) {
return new SqmExtractUnit<>( DAY_OF_YEAR, resolveExpressableTypeBasic( Integer.class ), nodeBuilder );
}
return super.visitDayField(ctx);
}
@Override
public Object visitWeekField(HqlParser.WeekFieldContext ctx) {
NodeBuilder nodeBuilder = creationContext.getNodeBuilder();
if (ctx.MONTH()!=null) {
final NodeBuilder nodeBuilder = creationContext.getNodeBuilder();
if ( ctx.MONTH() != null ) {
//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 );
}
if (ctx.YEAR()!=null) {
if ( ctx.YEAR() != null ) {
//this is computed from DAY_OF_YEAR/7
return new SqmExtractUnit<>(WEEK_OF_YEAR, resolveExpressableTypeBasic( Integer.class ), nodeBuilder);
return new SqmExtractUnit<>( WEEK_OF_YEAR, resolveExpressableTypeBasic( Integer.class ), nodeBuilder );
}
return super.visitWeekField(ctx);
}
@ -2256,41 +2595,259 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
@Override
public Object visitTimeZoneField(HqlParser.TimeZoneFieldContext ctx) {
NodeBuilder nodeBuilder = creationContext.getNodeBuilder();
if (ctx.HOUR()!=null) {
return new SqmExtractUnit<>(TIMEZONE_HOUR, resolveExpressableTypeBasic( Integer.class ), nodeBuilder);
if ( ctx.HOUR() != null ) {
return new SqmExtractUnit<>( TIMEZONE_HOUR, resolveExpressableTypeBasic( Integer.class ), nodeBuilder );
}
else if (ctx.MINUTE()!=null) {
return new SqmExtractUnit<>(TIMEZONE_MINUTE, resolveExpressableTypeBasic( Integer.class ), nodeBuilder);
else if ( ctx.MINUTE() != null ) {
return new SqmExtractUnit<>( TIMEZONE_MINUTE, resolveExpressableTypeBasic( Integer.class ), nodeBuilder );
}
else {
return new SqmExtractUnit<>( OFFSET, resolveExpressableTypeBasic( ZoneOffset.class ), nodeBuilder);
return new SqmExtractUnit<>( OFFSET, resolveExpressableTypeBasic( ZoneOffset.class ), nodeBuilder );
}
}
private boolean isExtractingJdbcTemporalType;
@Override
public Object visitExtractFunction(HqlParser.ExtractFunctionContext ctx) {
final SqmExpression<?> expressionToExtract = (SqmExpression) ctx.expression().accept( this );
final SqmExtractUnit<?> extractFieldExpression;
public Object visitExtractFunction(HqlParser.ExtractFunctionContext extractFunctionCtx) {
final SqmExpression<?> sqmTemporalExpr = (SqmExpression) extractFunctionCtx.expression().accept( this );
final SqmExtractUnit<?> sqmExtractUnit;
if ( ctx.extractField() != null ) {
extractFieldExpression = (SqmExtractUnit) ctx.extractField().accept( this);
// Allow `#visitDateOrTimeField()` to know if we're extracting from a JDBC Timestamp or from a
// 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 ) {
isExtractingJdbcTemporalType = isJdbcTemporalType( expressionToExtract.getNodeType() );
extractFieldExpression = (SqmExtractUnit) ctx.datetimeField().accept( this );
else if ( extractFunctionCtx.datetimeField() != null ) {
// for the shorter legacy Hibernate syntax "field(arg)"
sqmExtractUnit = (SqmExtractUnit) extractFunctionCtx.datetimeField().accept( this );
}
else {
return expressionToExtract;
return sqmTemporalExpr;
}
//noinspection unchecked
return new SqmFunction(
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
return new SqmFunction(
"extract",
getFunctionDescriptor( "extract" ),
sqmExtractUnit.getNodeType(),
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",
getFunctionDescriptor( "extract" ),
extractFieldExpression.getNodeType(),
asList( extractFieldExpression, expressionToExtract ),
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()
);
}
@ -2633,20 +3190,6 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
@Override
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 ) {
final String trimCharText = ctx.STRING_LITERAL().getText();
if ( trimCharText.length() != 1 ) {
@ -2971,6 +3514,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
}
}
//noinspection unchecked
SqmPath result = attribute.getElementPathSource().createSqmPath(
pluralAttributePath,
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.SqmExtractUnit;
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.SqmJpaCriteriaParameterWrapper;
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.SqmParameter;
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.Expression;
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.Star;
import org.hibernate.sql.ast.tree.expression.TrimSpecification;
@ -781,6 +784,10 @@ public abstract class BaseSqmToSqlAstConverter
@Override
public Expression visitLiteral(SqmLiteral literal) {
if ( literal instanceof SqmLiteralNull ) {
return new QueryLiteral( null, (BasicValuedMapping) inferableTypeAccessStack.getCurrent().get() );
}
return new QueryLiteral(
literal.getLiteralValue(),
(BasicValuedMapping) SqmMappingModelHelper.resolveMappingModelExpressable(
@ -939,13 +946,11 @@ public abstract class BaseSqmToSqlAstConverter
@Override
public Expression visitFunction(SqmFunction sqmFunction) {
final SqmFunctionDescriptor functionDescriptor = creationContext.getSessionFactory()
.getQueryEngine()
.getSqmFunctionRegistry()
.getFunctionDescriptor( sqmFunction.getFunctionName() );
final SqmFunctionDescriptor functionDescriptor = sqmFunction.getFunctionDescriptor();
shallownessStack.push( Shallowness.FUNCTION );
try {
//noinspection unchecked
return functionDescriptor.generateSqlExpression(
sqmFunction.getFunctionName(),
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
// public Object visitAbsFunction(SqmAbsFunction function) {
// shallownessStack.push( Shallowness.FUNCTION );
@ -1425,7 +1438,7 @@ public abstract class BaseSqmToSqlAstConverter
return new BinaryArithmeticExpression(
(Expression) expression.getLeftHandOperand().accept( this ), interpret( expression.getOperator() ),
(Expression) expression.getRightHandOperand().accept( this ),
determineValueMapping( expression )
(BasicValuedMapping) determineValueMapping( expression )
);
}
finally {

View File

@ -13,13 +13,13 @@ import java.util.function.Consumer;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.query.NavigablePath;
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.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.Expression;
import org.hibernate.sql.ast.tree.expression.SqlSelectionExpression;
@ -100,13 +100,11 @@ public class BasicValuedPathInterpretation<T> implements AssignableSqmPathInterp
assert tableGroup != null;
this.tableGroup = tableGroup;
}
@Override
public SqmPath<T> getInterpretedSqmPath() {
return sqmPath;
public NavigablePath getNavigablePath() {
return sqmPath.getNavigablePath();
}
@Override

View File

@ -13,6 +13,7 @@ import java.util.function.Consumer;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
@ -78,8 +79,8 @@ public class EmbeddableValuedPathInterpretation<T> implements AssignableSqmPathI
}
@Override
public SqmPath<T> getInterpretedSqmPath() {
return sqmPath;
public NavigablePath getNavigablePath() {
return sqmPath.getNavigablePath();
}
@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.ModelPart;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
@ -62,13 +63,13 @@ public class EntityValuedPathInterpretation<T> implements SqmPathInterpretation<
}
@Override
public SqmPath<T> getInterpretedSqmPath() {
return sqmPath;
public NavigablePath getNavigablePath() {
return sqmPath.getNavigablePath();
}
@Override
public ModelPart getExpressionType() {
return null;
return mapping;
}
@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.)
*
* @see org.hibernate.query.sqm.sql.SqmToSqlAstConverter
* @see #getInterpretedSqmPath
*
* @author Steve Ebersole
*/
public interface SqmPathInterpretation<T> extends Expression, DomainResultProducer<T> {
default NavigablePath getNavigablePath() {
return getInterpretedSqmPath().getNavigablePath();
}
SqmPath<T> getInterpretedSqmPath();
NavigablePath getNavigablePath();
@Override
ModelPart getExpressionType();

View File

@ -55,6 +55,10 @@ public class SqmFunction<T> extends AbstractSqmExpression<T> implements JpaFunct
this.arguments = arguments;
}
public SqmFunctionDescriptor getFunctionDescriptor() {
return functionDescriptor;
}
@Override
public String getFunctionName() {
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,
NodeBuilder nodeBuilder) {
super( nodeBuilder );
assert selectableNode != null;
this.selectableNode = selectableNode;
}
@ -31,6 +33,8 @@ public class SqmSelection<T> extends AbstractSqmNode implements SqmAliasedNode<T
String alias,
NodeBuilder nodeBuilder) {
super( nodeBuilder );
assert selectableNode != null;
this.selectableNode = selectableNode;
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.spi.JdbcParameterBinder;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
import org.hibernate.type.descriptor.sql.JdbcLiteralFormatter;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.sql.ast.spi.SqlAppender.CLOSE_PARENTHESIS;
@ -803,9 +804,11 @@ public abstract class AbstractSqlAstWalker
@Override
public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) {
appendSql( "(" );
arithmeticExpression.getLeftHandOperand().accept( this );
appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() );
arithmeticExpression.getRightHandOperand().accept( this );
appendSql( ")" );
}
@Override

View File

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

View File

@ -6,10 +6,17 @@
*/
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.sqm.sql.internal.DomainResultProducer;
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
@ -20,13 +27,13 @@ public class BinaryArithmeticExpression implements Expression, DomainResultProdu
private final BinaryArithmeticOperator operator;
private final Expression rhsOperand;
private final MappingModelExpressable resultType;
private final BasicValuedMapping resultType;
public BinaryArithmeticExpression(
Expression lhsOperand,
BinaryArithmeticOperator operator,
Expression rhsOperand,
MappingModelExpressable resultType) {
BasicValuedMapping resultType) {
this.operator = operator;
this.lhsOperand = lhsOperand;
this.rhsOperand = rhsOperand;
@ -34,7 +41,7 @@ public class BinaryArithmeticExpression implements Expression, DomainResultProdu
}
@Override
public MappingModelExpressable getExpressionType() {
public BasicValuedMapping getExpressionType() {
return resultType;
}
@ -43,22 +50,37 @@ public class BinaryArithmeticExpression implements Expression, DomainResultProdu
walker.visitBinaryArithmeticExpression( this );
}
// @Override
// public DomainResult createDomainResult(
// String resultVariable,
// DomainResultCreationState creationState) {
// final SqlSelection sqlSelection = creationState.getSqlExpressionResolver().resolveSqlSelection(
// this,
// getType().getJavaTypeDescriptor(),
// creationState.getSqlAstCreationState().getCreationContext().getDomainModel().getTypeConfiguration()
// );
// //noinspection unchecked
// return new ScalarDomainResultImpl(
// sqlSelection.getValuesArrayPosition(),
// resultVariable,
// resultType.getJavaTypeDescriptor()
// );
// }
@Override
public DomainResult createDomainResult(
String resultVariable,
DomainResultCreationState creationState) {
final SqlSelection sqlSelection = creationState.getSqlAstCreationState().getSqlExpressionResolver().resolveSqlSelection(
this,
resultType.getBasicType().getJavaTypeDescriptor(),
creationState.getSqlAstCreationState().getCreationContext().getDomainModel().getTypeConfiguration()
);
//noinspection unchecked
return new BasicResult(
sqlSelection.getValuesArrayPosition(),
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.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,10 +7,16 @@
package org.hibernate.type.descriptor;
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.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQueries;
import java.util.Locale;
import java.util.TimeZone;
@ -61,6 +67,16 @@ public final class DateTimeUtils {
.optionalStart().appendZoneOrOffsetId().optionalEnd()
.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.
*
@ -170,4 +186,79 @@ public final class DateTimeUtils {
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.ValueExtractor;
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.sql.internal.JdbcLiteralFormatterNumericData;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for {@link Types#BIGINT BIGINT} handling.
@ -38,6 +41,17 @@ public class BigIntTypeDescriptor implements SqlTypeDescriptor {
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
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
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.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for {@link Types#BIT BIT} handling.
@ -40,6 +42,11 @@ public class BitTypeDescriptor implements SqlTypeDescriptor {
return true;
}
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Boolean.class );
}
@Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
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.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for {@link Types#BLOB BLOB} handling.
@ -40,6 +42,11 @@ public abstract class BlobTypeDescriptor implements SqlTypeDescriptor {
return true;
}
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Blob.class );
}
@Override
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
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.ValueExtractor;
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.sql.internal.JdbcLiteralFormatterBoolean;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for {@link java.sql.Types#BOOLEAN BOOLEAN} handling.
@ -37,6 +40,17 @@ public class BooleanTypeDescriptor implements SqlTypeDescriptor {
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) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override

View File

@ -14,10 +14,15 @@ import java.sql.SQLException;
import java.sql.Types;
import java.util.Calendar;
import javax.persistence.TemporalType;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
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.sql.internal.JdbcLiteralFormatterTemporal;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for {@link Types#DATE DATE} handling.
@ -40,6 +45,17 @@ public class DateTypeDescriptor implements SqlTypeDescriptor {
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
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
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.ValueExtractor;
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.sql.internal.JdbcLiteralFormatterNumericData;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for {@link Types#DECIMAL DECIMAL} handling.
@ -39,6 +42,17 @@ public class DecimalTypeDescriptor implements SqlTypeDescriptor {
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
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
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.ValueExtractor;
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.sql.internal.JdbcLiteralFormatterNumericData;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for {@link Types#DOUBLE DOUBLE} handling.
@ -38,6 +41,17 @@ public class DoubleTypeDescriptor implements SqlTypeDescriptor {
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
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {

View File

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

View File

@ -15,7 +15,10 @@ import java.sql.Types;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
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.sql.internal.JdbcLiteralFormatterNumericData;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for {@link Types#INTEGER INTEGER} handling.
@ -38,6 +41,17 @@ public class IntegerTypeDescriptor implements SqlTypeDescriptor {
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
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {

View File

@ -4,7 +4,7 @@
* 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.sql.ast.spi;
package org.hibernate.type.descriptor.sql;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SharedSessionContractImplementor;

View File

@ -12,13 +12,15 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.sql.ast.spi.JdbcLiteralFormatter;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
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.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.
@ -41,9 +43,15 @@ public class NVarcharTypeDescriptor implements SqlTypeDescriptor {
return true;
}
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( String.class );
}
@Override
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

View File

@ -15,7 +15,10 @@ import java.sql.Types;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
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.sql.internal.JdbcLiteralFormatterNumericData;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for {@link Types#REAL REAL} handling.
@ -38,6 +41,17 @@ public class RealTypeDescriptor implements SqlTypeDescriptor {
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
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
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.ValueExtractor;
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.sql.internal.JdbcLiteralFormatterNumericData;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for {@link Types#SMALLINT SMALLINT} handling.
@ -38,6 +41,17 @@ public class SmallIntTypeDescriptor implements SqlTypeDescriptor {
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
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {

View File

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

View File

@ -14,10 +14,15 @@ import java.sql.Time;
import java.sql.Types;
import java.util.Calendar;
import javax.persistence.TemporalType;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
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.sql.internal.JdbcLiteralFormatterTemporal;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for {@link Types#TIME TIME} handling.
@ -40,6 +45,17 @@ public class TimeTypeDescriptor implements SqlTypeDescriptor {
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
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {

View File

@ -14,10 +14,15 @@ import java.sql.Timestamp;
import java.sql.Types;
import java.util.Calendar;
import javax.persistence.TemporalType;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
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.sql.internal.JdbcLiteralFormatterTemporal;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for {@link Types#TIMESTAMP TIMESTAMP} handling.
@ -40,6 +45,17 @@ public class TimestampTypeDescriptor implements SqlTypeDescriptor {
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
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
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.ValueExtractor;
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.sql.internal.JdbcLiteralFormatterNumericData;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for {@link Types#TINYINT TINYINT} handling.
@ -41,6 +44,17 @@ public class TinyIntTypeDescriptor implements SqlTypeDescriptor {
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
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
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.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for {@link Types#VARBINARY VARBINARY} handling.
@ -37,6 +39,11 @@ public class VarbinaryTypeDescriptor implements SqlTypeDescriptor {
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) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {

View File

@ -12,13 +12,13 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.sql.ast.spi.JdbcLiteralFormatter;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import static org.hibernate.sql.ast.spi.JdbcLiteralFormatter.NULL;
import org.hibernate.type.descriptor.sql.internal.JdbcLiteralFormatterCharacterData;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for {@link Types#VARCHAR VARCHAR} handling.
@ -41,9 +41,15 @@ public class VarcharTypeDescriptor implements SqlTypeDescriptor {
return true;
}
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( String.class );
}
@Override
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaTypeDescriptor<T> javaTypeDescriptor) {
return (value, dialect, session) -> value == null ? NULL : "'" + value.toString() + "'";
//noinspection unchecked
return new JdbcLiteralFormatterCharacterData( javaTypeDescriptor );
}
@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;
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.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;
/**
* @author Steve Ebersole
*/
public class LiteralTests extends BaseSqmUnitTest {
@SuppressWarnings("WeakerAccess")
@ServiceRegistry
@DomainModel( standardModels = StandardDomainModel.GAMBIT )
@SessionFactory
public class LiteralTests {
@Override
protected void applyMetadataSources(MetadataSources metadataSources) {
StandardDomainModel.GAMBIT.getDescriptor().applyDomainModel( metadataSources );
@Test
public void testJdbcTimeLiteral(SessionFactoryScope scope) {
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
public void testTimestampLiteral() {
final SqmSelectStatement sqm = interpretSelect( "from EntityOfBasics e1 where e1.theTimestamp = {ts '2018-01-01T12:30:00'}" );
public void testJdbcDateLiteral(SessionFactoryScope scope) {
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
public void testDateLiteral() {
final SqmSelectStatement sqm = interpretSelect( "from EntityOfBasics e1 where e1.theDate = {d '2018-01-01'}" );
public void testJdbcTimestampLiteral(SessionFactoryScope scope) {
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
public void testTimeLiteral() {
final SqmSelectStatement sqm = interpretSelect( "from EntityOfBasics e1 where e1.theTime = {t '12:30:00'}" );
public void testLocalDateLiteral(SessionFactoryScope scope) {
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
@FailureExpected
public void testCoalesceFunction(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
@ -617,119 +616,175 @@ public class StandardFunctionTests {
public void isolated(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery("select extract(time from local_datetime), extract(date from local_datetime) from EntityOfBasics e")
.list();
session.createQuery("select extract(time from e.theTimestamp), extract(date from e.theTimestamp) from EntityOfBasics e").list();
session.createQuery("select extract(time from local_datetime), extract(date from local_datetime) from EntityOfBasics e").list();
}
);
}
@Test
@FailureExpected
// @FailureExpected
public void testExtractFunctionWithAssertions(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
EntityOfBasics entity = new EntityOfBasics();
entity.setId(1);
session.save(entity);
session.flush();
assertThat(
session.createQuery("select extract(week of year from date '2019-01-01') from EntityOfBasics").getResultList().get(0),
is(1)
);
assertThat(
session.createQuery("select extract(week of year from date '2019-01-05') from EntityOfBasics").getResultList().get(0),
is(1)
);
assertThat(
session.createQuery("select extract(week of year from date '2019-01-06') from EntityOfBasics").getResultList().get(0),
is(2)
);
assertThat(
session.createQuery("select extract(week of month from date '2019-05-01') from EntityOfBasics").getResultList().get(0),
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(
session.createQuery("select extract(week from date '2019-05-27') from EntityOfBasics").getResultList().get(0),
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(
session.createQuery("select extract(day of year from date '2019-05-30') from EntityOfBasics").getResultList().get(0),
is(150)
);
assertThat(
session.createQuery("select extract(day of month from date '2019-05-27') from EntityOfBasics").getResultList().get(0),
is(27)
);
assertThat(
session.createQuery("select extract(day from date '2019-05-31') from EntityOfBasics").getResultList().get(0),
is(31)
);
assertThat(
session.createQuery("select extract(month from date '2019-05-31') from EntityOfBasics").getResultList().get(0),
is(5)
);
assertThat(
session.createQuery("select extract(year from date '2019-05-31') from EntityOfBasics").getResultList().get(0),
is(2019)
);
assertThat(
session.createQuery("select extract(quarter from date '2019-05-31') from EntityOfBasics").getResultList().get(0),
is(2)
);
assertThat(
session.createQuery("select extract(day of week from date '2019-05-27') from EntityOfBasics").getResultList().get(0),
is(2)
);
assertThat(
session.createQuery("select extract(day of week from date '2019-05-31') from EntityOfBasics").getResultList().get(0),
is(6)
);
assertThat(
session.createQuery("select extract(second from time '14:12:10') from EntityOfBasics").getResultList().get(0),
is(10f)
);
assertThat(
session.createQuery("select extract(minute from time '14:12:10') from EntityOfBasics").getResultList().get(0),
is(12)
);
assertThat(
session.createQuery("select extract(hour from time '14:12:10') from EntityOfBasics").getResultList().get(0),
is(14)
);
assertThat(
session.createQuery("select extract(date from current datetime) from EntityOfBasics").getResultList().get(0),
instanceOf( LocalDate.class)
);
assertThat(
session.createQuery("select extract(time from current datetime) from EntityOfBasics").getResultList().get(0),
instanceOf( LocalTime.class)
);
session.delete(entity);
}
);
try {
scope.inTransaction(
session -> {
assertThat(
session.createQuery(
"select extract(week of year from {2019-01-01}) from EntityOfBasics b where b.id = 1" )
.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(
session.createQuery(
"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(
session.createQuery(
"select extract(week of year from {2019-01-05}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 1 )
);
assertThat(
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 )
);
assertThat(
session.createQuery(
"select extract(day of year from {2019-05-30}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 150 )
);
assertThat(
session.createQuery(
"select extract(day of month from {2019-05-27}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 27 )
);
assertThat(
session.createQuery( "select extract(day from {2019-05-31}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 31 )
);
assertThat(
session.createQuery( "select extract(month from {2019-05-31}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 5 )
);
assertThat(
session.createQuery( "select extract(year from {2019-05-31}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 2019 )
);
assertThat(
session.createQuery(
"select extract(quarter from {2019-05-31}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 2 )
);
assertThat(
session.createQuery(
"select extract(day of week from {2019-05-27}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 2 )
);
assertThat(
session.createQuery(
"select extract(day of week from {2019-05-31}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 6 )
);
assertThat(
session.createQuery( "select extract(second from {14:12:10}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 10f )
);
assertThat(
session.createQuery( "select extract(minute from {14:12:10}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 12 )
);
assertThat(
session.createQuery( "select extract(hour from {14:12:10}) from EntityOfBasics" )
.getResultList()
.get( 0 ),
is( 14 )
);
assertThat(
session.createQuery( "select extract(date from local_datetime) from EntityOfBasics" )
.getResultList()
.get( 0 ),
instanceOf( LocalDate.class )
);
assertThat(
session.createQuery( "select extract(time from local_datetime) from EntityOfBasics" )
.getResultList()
.get( 0 ),
instanceOf( LocalTime.class )
);
}
);
}
finally {
scope.inTransaction(
session -> {
session.createQuery( "delete from EntityOfBasics" ).executeUpdate();
}
);
}
}
@Test
@ -795,14 +850,8 @@ public class StandardFunctionTests {
public void testCountFunction(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery("select count(*) 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();
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
@FailureExpected
public void testFormat(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
@ -829,25 +877,45 @@ public class StandardFunctionTests {
entity.setTheDate( new Date( 74, 2, 25 ) );
entity.setTheTime( new Time( 23, 10, 8 ) );
entity.setTheTimestamp( new Timestamp( System.currentTimeMillis() ) );
session.persist(entity);
session.flush();
session.createQuery("select format(e.theTime as 'hh:mm:ss aa') from EntityOfBasics e")
.list();
session.createQuery("select format(e.theDate as 'dd/MM/yy'), format(e.theDate as 'EEEE, MMMM dd, yyyy') from EntityOfBasics e")
.list();
session.createQuery("select format(e.theTimestamp as 'dd/MM/yyyy ''at'' HH:mm:ss') from EntityOfBasics e")
.list();
assertThat(
session.createQuery("select format(e.theDate as 'EEEE, dd/MM/yyyy') from EntityOfBasics").getResultList().get(0),
is("Monday, 25/03/1974")
);
assertThat(
session.createQuery("select format(e.theTime as '''Hello'', hh:mm:ss aa') from EntityOfBasics").getResultList().get(0),
is("Hello, 11:10:08 PM")
);
session.persist( entity );
}
);
try {
scope.inTransaction(
session -> {
session.createQuery( "select format(e.theTime as 'hh:mm:ss aa') from EntityOfBasics e" )
.list();
session.createQuery(
"select format(e.theDate as 'dd/MM/yy'), format(e.theDate as 'EEEE, MMMM dd, yyyy') from EntityOfBasics e" )
.list();
session.createQuery(
"select format(e.theTimestamp as 'dd/MM/yyyy ''at'' HH:mm:ss') from EntityOfBasics e" )
.list();
assertThat(
session.createQuery(
"select format(e.theDate as 'EEEE, dd/MM/yyyy') from EntityOfBasics e" )
.getResultList()
.get( 0 ),
is( "Monday, 25/03/1974" )
);
assertThat(
session.createQuery(
"select format(e.theTime as '''Hello'', hh:mm:ss aa') from EntityOfBasics e" )
.getResultList()
.get( 0 ),
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.sql.Clob;
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 javax.persistence.AttributeConverter;
import javax.persistence.Column;
@ -42,6 +47,11 @@ public class EntityOfBasics {
private Date theTime;
private Date theTimestamp;
private Instant theInstant;
private LocalDateTime theLocalDateTime;
private LocalDate theLocalDate;
private LocalTime theLocalTime;
private OffsetDateTime theOffsetDateTime;
private ZonedDateTime theZonedDateTime;
private Gender gender;
private Gender convertedGender;
private Gender ordinalGender;
@ -167,6 +177,46 @@ public class EntityOfBasics {
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> {
@Override
public Character convertToDatabaseColumn(Gender attribute) {