Repurpose DefaultSizeStrategy to SizeStrategy for resolving final size. Fix boolean encoding/decoding issues. Remove duplicate order bys. Fix set operation nesting. Fix lots of tests for SQLServer, MariaDB, Derby and Oracle

This commit is contained in:
Christian Beikov 2021-02-10 13:03:26 +01:00
parent 019f934344
commit 9f096e89ec
97 changed files with 1917 additions and 1016 deletions

View File

@ -2,12 +2,12 @@
mysql_5_7() {
docker rm -f mysql || true
docker run --name mysql -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -p3306:3306 -d mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
docker run --name mysql -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=hibernate_orm_test -p3306:3306 -d mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
}
mysql_8_0() {
docker rm -f mysql || true
docker run --name mysql -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -p3306:3306 -d mysql:8.0.21 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
docker run --name mysql -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=hibernate_orm_test -p3306:3306 -d mysql:8.0.21 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
}
mariadb() {

View File

@ -75,13 +75,13 @@ values
// QUERY SPEC - general structure of root sqm or sub sqm
queryExpression
: queryGroup queryOrder?
: simpleQueryExpression # SimpleQueryGroup
| queryExpression (setOperator simpleQueryExpression)+ # SetQueryGroup
;
queryGroup
: querySpec # QuerySpecQueryGroup
| LEFT_PAREN queryGroup RIGHT_PAREN # NestedQueryGroup
| queryGroup queryOrder? (setOperator (querySpec | LEFT_PAREN queryGroup RIGHT_PAREN))+ # SetQueryGroup
simpleQueryExpression
: querySpec queryOrder? # QuerySpecExpression
| LEFT_PAREN queryExpression RIGHT_PAREN queryOrder? # NestedQueryExpression
;
setOperator

View File

@ -225,6 +225,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
private final boolean procedureParameterNullPassingEnabled;
private final boolean collectionJoinSubqueryRewriteEnabled;
private final boolean omitJoinOfSuperclassTablesEnabled;
private final int preferredSqlTypeCodeForBoolean;
// Caching
private boolean secondLevelCacheEnabled;
@ -415,6 +416,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
this.procedureParameterNullPassingEnabled = cfgService.getSetting( PROCEDURE_NULL_PARAM_PASSING, BOOLEAN, false );
this.collectionJoinSubqueryRewriteEnabled = cfgService.getSetting( COLLECTION_JOIN_SUBQUERY, BOOLEAN, true );
this.omitJoinOfSuperclassTablesEnabled = cfgService.getSetting( OMIT_JOIN_OF_SUPERCLASS_TABLES, BOOLEAN, true );
this.preferredSqlTypeCodeForBoolean = ConfigurationHelper.getPreferredSqlTypeCodeForBoolean( serviceRegistry );
final RegionFactory regionFactory = serviceRegistry.getService( RegionFactory.class );
if ( !NoCachingRegionFactory.class.isInstance( regionFactory ) ) {
@ -1204,8 +1206,11 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
return omitJoinOfSuperclassTablesEnabled;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public int getPreferredSqlTypeCodeForBoolean() {
return preferredSqlTypeCodeForBoolean;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// In-flight mutation access
public void applyBeanManager(Object beanManager) {

View File

@ -156,7 +156,7 @@ public class InferredBasicValueResolver {
}
@SuppressWarnings("rawtypes")
private static BasicType<?> resolveSqlTypeIndicators(
public static BasicType<?> resolveSqlTypeIndicators(
SqlTypeDescriptorIndicators stdIndicators,
BasicType<?> resolved) {
if ( resolved instanceof SqlTypeDescriptorIndicatorCapable ) {

View File

@ -143,14 +143,12 @@ public class RelationalObjectBinder {
if ( columnSource.getSizeSource().getLength() != null ) {
column.setLength( columnSource.getSizeSource().getLength() );
}
final Integer precision = columnSource.getSizeSource().getPrecision();
if ( columnSource.getSizeSource().getScale() != null ) {
if ( precision != null && precision > 0 ) {
column.setPrecision( precision );
column.setScale( columnSource.getSizeSource().getScale() );
}
if ( columnSource.getSizeSource().getPrecision() != null ) {
column.setPrecision( columnSource.getSizeSource().getPrecision() );
}
}
column.setNullable( interpretNullability( columnSource.isNullable(), areColumnsNullableByDefault ) );

View File

@ -484,4 +484,9 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp
public boolean isOmitJoinOfSuperclassTablesEnabled() {
return delegate.isOmitJoinOfSuperclassTablesEnabled();
}
@Override
public int getPreferredSqlTypeCodeForBoolean() {
return delegate.getPreferredSqlTypeCodeForBoolean();
}
}

View File

@ -358,4 +358,6 @@ public interface SessionFactoryOptions extends QueryEngineOptions {
}
boolean isOmitJoinOfSuperclassTablesEnabled();
int getPreferredSqlTypeCodeForBoolean();
}

View File

@ -233,7 +233,7 @@ public class Ejb3Column {
this.mappingColumn = new Column();
redefineColumnName( columnName, propertyName, applyNamingStrategy );
this.mappingColumn.setLength( length );
if ( precision!=null ) { //relevant precision
if ( precision != null && precision > 0 ) { //relevant precision
this.mappingColumn.setPrecision( precision );
this.mappingColumn.setScale( scale );
}

View File

@ -1539,7 +1539,7 @@ public abstract class AbstractHANADialect extends Dialect {
@Override
public String translateDatetimeFormat(String format) {
//I don't think HANA needs FM
return OracleDialect.datetimeFormat( format, false ).result();
return OracleDialect.datetimeFormat( format, false, false ).result();
}
public boolean isUseUnicodeStringTypes() {

View File

@ -12,6 +12,7 @@ import org.hibernate.NullOrdering;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.IndividualLeastGreatestEmulation;
import org.hibernate.dialect.identity.AbstractTransactSQLIdentityColumnSupport;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.metamodel.mapping.EntityMappingType;
@ -100,6 +101,9 @@ abstract class AbstractTransactSQLDialect extends Dialect {
CommonFunctionFactory.substring_substringLen( queryEngine );
CommonFunctionFactory.datepartDatename( queryEngine );
CommonFunctionFactory.lastDay_eomonth( queryEngine );
queryEngine.getSqmFunctionRegistry().register( "least", new IndividualLeastGreatestEmulation( true ) );
queryEngine.getSqmFunctionRegistry().register( "greatest", new IndividualLeastGreatestEmulation( false ) );
}
@Override

View File

@ -0,0 +1,104 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.dialect;
import org.hibernate.query.CastType;
/**
* Utility for decoding boolean representations.
*
* @author Christian Beikov
*/
public final class BooleanDecoder {
public static String toInteger(CastType from) {
switch ( from ) {
case BOOLEAN:
return "decode(?1,false,0,true,1,null)";
case YN_BOOLEAN:
return "decode(?1,'Y',1,'N',0,null)";
case TF_BOOLEAN:
return "decode(?1,'T',1,'F',0,null)";
}
return null;
}
public static String toBoolean(CastType from) {
switch ( from ) {
case STRING:
return "decode(?1,'T',true,'F',false,'Y',true,'N',false,null)";
case YN_BOOLEAN:
return "decode(?1,'Y',true,'N',false,null)";
case TF_BOOLEAN:
return "decode(?1,'T',true,'F',false,null)";
case INTEGER:
case LONG:
case INTEGER_BOOLEAN:
return "decode(?1,abs(sign(?1)),1,true,0,false,null)";
}
return null;
}
public static String toIntegerBoolean(CastType from) {
switch ( from ) {
case STRING:
return "decode(?1,'T',1,'F',0,'Y',1,'N',0,null)";
case YN_BOOLEAN:
return "decode(?1,'Y',1,'N',0,null)";
case TF_BOOLEAN:
return "decode(?1,'T',1,'F',0,null)";
case INTEGER:
case LONG:
return "abs(sign(?1))";
}
return null;
}
public static String toYesNoBoolean(CastType from) {
switch ( from ) {
case STRING:
return "decode(?1,'T','Y','F','N','Y','Y','N','N',null)";
case INTEGER_BOOLEAN:
return "decode(?1,1,'Y',0,'N',null)";
case TF_BOOLEAN:
return "decode(?1,'T','Y','F','N',null)";
case INTEGER:
case LONG:
return "decode(abs(sign(?1)),1,'Y',0,'N',null)";
}
return null;
}
public static String toTrueFalseBoolean(CastType from) {
switch ( from ) {
case STRING:
return "decode(?1,'T','T','F','F','Y','T','N','F',null)";
case INTEGER_BOOLEAN:
return "decode(?1,1,'T',0,'F',null)";
case YN_BOOLEAN:
return "decode(?1,'Y','T','N','F',null)";
case INTEGER:
case LONG:
return "decode(abs(sign(?1)),1,'T',0,'F',null)";
}
return null;
}
public static String toString(CastType from) {
switch ( from ) {
case INTEGER_BOOLEAN:
return "decode(?1,0,'false',1,'true',null)";
case TF_BOOLEAN:
return "decode(?1,'T','true','F','false',null)";
case YN_BOOLEAN:
return "decode(?1,'Y','true','N','false',null)";
case BOOLEAN:
return "decode(?1,true,'true',false,'false',null)";
}
return null;
}
}

View File

@ -304,7 +304,7 @@ public class CUBRIDDialect extends Dialect {
public String translateDatetimeFormat(String format) {
//I do not know if CUBRID supports FM, but it
//seems that it does pad by default, so it needs it!
return OracleDialect.datetimeFormat( format, true )
return OracleDialect.datetimeFormat( format, true, false )
.replace("SSSSSS", "FF")
.replace("SSSSS", "FF")
.replace("SSSS", "FF")

View File

@ -394,7 +394,7 @@ public class CacheDialect extends Dialect {
@Override
public String translateDatetimeFormat(String format) {
//I don't think Cache needs FM
return OracleDialect.datetimeFormat( format, false ).result();
return OracleDialect.datetimeFormat( format, false, false ).result();
}
}

View File

@ -28,7 +28,6 @@ import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.query.CastType;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.mutation.internal.cte.CteStrategy;
@ -661,11 +660,26 @@ public class DB2Dialect extends Dialect {
return new DB2IdentityColumnSupport();
}
@Override
public boolean supportsValuesList() {
return true;
}
@Override
public boolean supportsRowValueConstructorSyntaxInInList() {
return false;
}
@Override
public boolean supportsPartitionBy() {
return true;
}
@Override
public boolean supportsNonQueryWithCTE() {
return true;
}
@Override
public GroupBySummarizationRenderingStrategy getGroupBySummarizationRenderingStrategy() {
return GroupBySummarizationRenderingStrategy.FUNCTION;
@ -679,7 +693,7 @@ public class DB2Dialect extends Dialect {
@Override
public String translateDatetimeFormat(String format) {
//DB2 does not need nor support FM
return OracleDialect.datetimeFormat( format, false ).result();
return OracleDialect.datetimeFormat( format, false, false ).result();
}
@Override
@ -703,16 +717,6 @@ public class DB2Dialect extends Dialect {
}
}
@Override
public String castPattern(CastType from, CastType to) {
if ( getVersion() < 1100 && from == CastType.BOOLEAN && to == CastType.STRING ) {
return "case when ?1 = 1 then 'true' else 'false' end";
}
else {
return super.castPattern( from, to );
}
}
@Override
public String extractPattern(TemporalUnit unit) {
if ( unit == TemporalUnit.WEEK ) {

View File

@ -12,6 +12,8 @@ import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.DerbyConcatEmulation;
import org.hibernate.dialect.function.IndividualLeastGreatestEmulation;
import org.hibernate.dialect.function.InsertSubstringOverlayEmulation;
import org.hibernate.dialect.identity.DB2IdentityColumnSupport;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.pagination.AbstractLimitHandler;
@ -30,7 +32,6 @@ import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.query.CastType;
import org.hibernate.query.CastTypeKind;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
@ -60,8 +61,6 @@ import java.sql.Types;
import javax.persistence.TemporalType;
import static org.hibernate.query.CastType.BOOLEAN;
/**
* Hibernate Dialect for Apache Derby / Cloudscape 10
*
@ -149,6 +148,13 @@ public class DerbyDialect extends Dialect {
return 31;
}
@Override
public int getPreferredSqlTypeCodeForBoolean() {
return getVersion() < 1070
? Types.SMALLINT
: Types.BOOLEAN;
}
@Override
public int getVersion() {
return version;
@ -196,6 +202,7 @@ public class DerbyDialect extends Dialect {
queryEngine.getSqmFunctionRegistry().register( "concat", new DerbyConcatEmulation() );
//no way I can see to pad with anything other than spaces
// TODO: To support parameters, we have to inline the values
queryEngine.getSqmFunctionRegistry().patternDescriptorBuilder( "lpad", "case when length(?1)<?2 then substr(char('',?2)||?1,length(?1)+1) else ?1 end" )
.setInvariantType( StandardBasicTypes.STRING )
.setExactArgumentCount( 2 )
@ -206,6 +213,10 @@ public class DerbyDialect extends Dialect {
.setExactArgumentCount( 2 )
.setArgumentListSignature("(string, length)")
.register();
queryEngine.getSqmFunctionRegistry().register( "least", new IndividualLeastGreatestEmulation( true ) );
queryEngine.getSqmFunctionRegistry().register( "greatest", new IndividualLeastGreatestEmulation( false ) );
queryEngine.getSqmFunctionRegistry().register( "overlay", new InsertSubstringOverlayEmulation( true ) );
}
@Override
@ -245,7 +256,14 @@ public class DerbyDialect extends Dialect {
case DAY_OF_YEAR:
return "({fn timestampdiff(sql_tsi_day, date(char(year(?2),4)||'-01-01'),?2)}+1)";
case DAY_OF_WEEK:
return "(mod({fn timestampdiff(sql_tsi_day, {d '2000-01-01'}, ?2)},7)+1)";
// Use the approach as outlined here: https://stackoverflow.com/questions/36357013/day-of-week-from-seconds-since-epoch
return "(mod(mod({fn timestampdiff(sql_tsi_day, {d '1970-01-01'}, ?2)}+4,7)+7,7)+1)";
case WEEK:
// Use the approach as outlined here: https://www.sqlservercentral.com/articles/a-simple-formula-to-calculate-the-iso-week-number
// In SQL Server terms this is (DATEPART(dy,DATEADD(dd,DATEDIFF(dd,'17530101',@SomeDate)/7*7,'17530104'))+6)/7
return "(({fn timestampdiff(sql_tsi_day, date(char(year(?2),4)||'-01-01'),{fn timestampadd(sql_tsi_day, {fn timestampdiff(sql_tsi_day, {d '1753-01-01'}, ?2)}/7*7, {d '1753-01-04'})})}+7)/7)";
case QUARTER:
return "((month(?2)+2)/3)";
default:
return "?1(?2)";
}
@ -273,36 +291,30 @@ public class DerbyDialect extends Dialect {
*/
@Override
public String castPattern(CastType from, CastType to) {
switch (to) {
switch ( to ) {
case FLOAT:
return "cast(double(?1) as real)";
case DOUBLE:
return "double(?1)";
case BOOLEAN:
switch (from) {
case STRING:
// return "case when lower(?1)in('t','true') then true when lower(?1)in('f','false') then false end";
return "case when ?1 in('t','true','T','TRUE') then true when ?1 in('f','false','F','FALSE') then false end";
case LONG:
case INTEGER:
return "(?1<>0)";
}
break;
case INTEGER:
case LONG:
if ( from == BOOLEAN && getVersion() >= 1070 ) {
return "case ?1 when false then 0 when true then 1 end";
}
break;
case STRING:
// Derby madness http://db.apache.org/derby/docs/10.8/ref/rrefsqlj33562.html
// With a nice rant: https://blog.jooq.org/2011/10/29/derby-casting-madness-the-sequel/
// See https://issues.apache.org/jira/browse/DERBY-2072
if ( from.getKind() == CastTypeKind.NUMERIC ) {
// Use the maximum char capacity here as an intermediate type because Derby doesn't support direct conversion to varchar
return "cast(cast(?1 as char(254)) as ?2)";
// Since numerics can't be cast to varchar directly, use char(254) i.e. with the maximum char capacity
// as an intermediate type before converting to varchar
switch ( from ) {
case FLOAT:
case DOUBLE:
// Derby can't cast to char directly, but needs to be cast to decimal first...
return "cast(trim(cast(cast(?1 as decimal) as char(254))) as ?2)";
case INTEGER:
case FIXED:
return "cast(trim(cast(?1 as char(254))) as ?2)";
}
break;
}
return super.castPattern(from, to);
return super.castPattern( from, to );
}
@Override
@ -357,8 +369,8 @@ public class DerbyDialect extends Dialect {
@Override
public String getQuerySequencesString() {
return getVersion() < 1060
? "select sys.sysschemas.schemaname as sequence_schema, sys.syssequences.* from sys.syssequences left join sys.sysschemas on sys.syssequences.schemaid = sys.sysschemas.schemaid"
: null;
? null
: "select sys.sysschemas.schemaname as sequence_schema, sys.syssequences.* from sys.syssequences left join sys.sysschemas on sys.syssequences.schemaid = sys.sysschemas.schemaid";
}
@Override

View File

@ -74,7 +74,6 @@ import org.hibernate.sql.*;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.spi.ANSICaseExpressionWalker;
import org.hibernate.sql.ast.spi.CaseExpressionWalker;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
@ -152,7 +151,7 @@ public abstract class Dialect implements ConversionContext {
private final UniqueDelegate uniqueDelegate;
private DefaultSizeStrategy defaultSizeStrategy;
private final SizeStrategy sizeStrategy;
// constructors and factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -242,7 +241,7 @@ public abstract class Dialect implements ConversionContext {
}
uniqueDelegate = new DefaultUniqueDelegate( this );
defaultSizeStrategy = new DefaultSizeStrategyImpl();
sizeStrategy = new SizeStrategyImpl();
}
/**
@ -442,7 +441,7 @@ public abstract class Dialect implements ConversionContext {
//very few databases support ANSI-style overlay() function, so emulate
//it here in terms of either insert() or concat()/substring()
queryEngine.getSqmFunctionRegistry().register("overlay", new InsertSubstringOverlayEmulation());
queryEngine.getSqmFunctionRegistry().register("overlay", new InsertSubstringOverlayEmulation( false ));
//ANSI SQL trim() function is supported on almost all of the databases
//we care about, but on some it must be emulated using ltrim(), rtrim(),
@ -634,6 +633,88 @@ public abstract class Dialect implements ConversionContext {
* type the value argument is cast to
*/
public String castPattern(CastType from, CastType to) {
switch ( to ) {
case STRING:
switch ( from ) {
case INTEGER_BOOLEAN:
return "case ?1 when 1 then 'true' when 0 then 'false' else null end";
case YN_BOOLEAN:
return "case ?1 when 'Y' then 'true' when 'N' then 'false' else null end";
case TF_BOOLEAN:
return "case ?1 when 'T' then 'true' when 'F' then 'false' else null end";
}
break;
case INTEGER:
case LONG:
switch ( from ) {
case YN_BOOLEAN:
return "case ?1 when 'Y' then 1 when 'N' then 0 else null end";
case TF_BOOLEAN:
return "case ?1 when 'T' then 1 when 'F' then 0 else null end";
case BOOLEAN:
return "case ?1 when true then 1 when false then 0 else null end";
}
break;
case INTEGER_BOOLEAN:
switch ( from ) {
case STRING:
return "case ?1 when 'T' then 1 when 'Y' then 1 when 'F' then 0 when 'N' then 0 else null end";
case INTEGER:
case LONG:
return "abs(sign(?1))";
case YN_BOOLEAN:
return "case ?1 when 'Y' then 1 when 'N' then 0 else null end";
case TF_BOOLEAN:
return "case ?1 when 'T' then 1 when 'F' then 0 else null end";
case BOOLEAN:
return "case ?1 when true then 1 when false then 0 else null end";
}
break;
case YN_BOOLEAN:
switch ( from ) {
case STRING:
return "case ?1 when 'T' then 'Y' when 'Y' then 'Y' when 'F' then 'N' when 'N' then 'N' else null end";
case INTEGER_BOOLEAN:
return "case ?1 when 1 then 'Y' when 0 then 'N' else null end";
case INTEGER:
case LONG:
return "case abs(sign(?1)) when 1 then 'Y' when 0 then 'N' else null end";
case TF_BOOLEAN:
return "case ?1 when 'T' then 'Y' when 'F' then 'N' else null end";
case BOOLEAN:
return "case ?1 when true then 'Y' when false then 'N' else null end";
}
break;
case TF_BOOLEAN:
switch ( from ) {
case STRING:
return "case ?1 when 'T' then 'T' when 'Y' then 'T' when 'F' then 'F' when 'N' then 'F' else null end";
case INTEGER_BOOLEAN:
return "case ?1 when 1 then 'T' when 0 then 'F' else null end";
case INTEGER:
case LONG:
return "case abs(sign(?1)) when 1 then 'T' when 0 then 'F' else null end";
case YN_BOOLEAN:
return "case ?1 when 'Y' then 'T' when 'N' then 'F' else null end";
case BOOLEAN:
return "case ?1 when true then 'T' when false then 'F' else null end";
}
break;
case BOOLEAN:
switch ( from ) {
case STRING:
return "case ?1 when 'T' then true when 'Y' then true when 'F' then false when 'N' then false else null end";
case INTEGER_BOOLEAN:
case INTEGER:
case LONG:
return "(?1<>0)";
case YN_BOOLEAN:
return "(?1<>'N')";
case TF_BOOLEAN:
return "(?1<>'F')";
}
break;
}
return "cast(?1 as ?2)";
}
@ -913,9 +994,12 @@ public abstract class Dialect implements ConversionContext {
Size size;
if ( length == null && precision == null ) {
//use defaults
size = getDefaultSizeStrategy().resolveDefaultSize(
size = getSizeStrategy().resolveSize(
type.getJdbcMapping().getSqlTypeDescriptor(),
type.getJdbcMapping().getJavaTypeDescriptor()
type.getJdbcMapping().getJavaTypeDescriptor(),
precision,
scale,
length
);
}
else {
@ -3399,12 +3483,8 @@ public abstract class Dialect implements ConversionContext {
return false;
}
public DefaultSizeStrategy getDefaultSizeStrategy(){
return defaultSizeStrategy;
}
public void setDefaultSizeStrategy(DefaultSizeStrategy defaultSizeStrategy){
this.defaultSizeStrategy = defaultSizeStrategy;
public SizeStrategy getSizeStrategy() {
return sizeStrategy;
}
public long getDefaultLobLength() {
@ -3519,38 +3599,63 @@ public abstract class Dialect implements ConversionContext {
/**
* Pluggable strategy for determining the Size to use for columns of
* a given SQL type when no explicit Size has been given.
* a given SQL type.
*
* Allows Dialects, integrators and users a chance to apply default
* column size limits in certain situations based on the mapped
* Allows Dialects, integrators and users a chance to apply
* column size defaults and limits in certain situations based on the mapped
* SQL and Java types. E.g. when mapping a UUID to a VARCHAR column
* we know the default Size should be `Size#length == 36`.
*/
public interface DefaultSizeStrategy {
public interface SizeStrategy {
/**
* Resolve the default {@link Size} to use for columns of the given
* Resolve the {@link Size} to use for columns of the given
* {@link SqlTypeDescriptor SQL type} and {@link JavaTypeDescriptor Java type}.
*
* @return a non-null {@link Size}
*/
Size resolveDefaultSize(SqlTypeDescriptor sqlType, JavaTypeDescriptor javaType);
Size resolveSize(
SqlTypeDescriptor sqlType,
JavaTypeDescriptor javaType,
Integer precision,
Integer scale, Long length);
}
public class DefaultSizeStrategyImpl implements DefaultSizeStrategy {
public class SizeStrategyImpl implements SizeStrategy {
@Override
public Size resolveDefaultSize(SqlTypeDescriptor sqlType, JavaTypeDescriptor javaType) {
public Size resolveSize(
SqlTypeDescriptor sqlType,
JavaTypeDescriptor javaType,
Integer precision,
Integer scale,
Long length) {
final Size size = new Size();
int jdbcTypeCode = sqlType.getJdbcTypeCode();
switch (jdbcTypeCode) {
case Types.BIT:
// Use the default length for Boolean if we encounter the JPA default 255 instead
if ( javaType.getJavaType() == Boolean.class && length != null && length == 255 ) {
length = null;
}
size.setLength( javaType.getDefaultSqlLength( Dialect.this ) );
break;
case Types.CHAR:
// Use the default length for char if we encounter the JPA default 255 instead
if ( javaType.getJavaType() == Character.class && length != null && length == 255 ) {
length = null;
}
size.setLength( javaType.getDefaultSqlLength( Dialect.this ) );
break;
case Types.NCHAR:
case Types.BINARY:
case Types.VARCHAR:
case Types.NVARCHAR:
case Types.VARBINARY:
size.setLength( javaType.getDefaultSqlLength(Dialect.this) );
// Use the default length for UUID if we encounter the JPA default 255 instead
if ( javaType.getJavaType() == UUID.class && length != null && length == 255 ) {
length = null;
}
size.setLength( javaType.getDefaultSqlLength( Dialect.this ) );
break;
case Types.LONGVARCHAR:
case Types.LONGNVARCHAR:
@ -3562,11 +3667,11 @@ public abstract class Dialect implements ConversionContext {
case Types.REAL:
case Types.TIMESTAMP:
case Types.TIMESTAMP_WITH_TIMEZONE:
size.setPrecision( javaType.getDefaultSqlPrecision(Dialect.this) );
size.setPrecision( javaType.getDefaultSqlPrecision( Dialect.this ) );
break;
case Types.NUMERIC:
case Types.DECIMAL:
size.setPrecision( javaType.getDefaultSqlPrecision(Dialect.this) );
size.setPrecision( javaType.getDefaultSqlPrecision( Dialect.this ) );
size.setScale( javaType.getDefaultSqlScale() );
break;
case Types.CLOB:
@ -3575,6 +3680,16 @@ public abstract class Dialect implements ConversionContext {
break;
}
if ( precision != null ) {
size.setPrecision( precision );
}
if ( scale != null ) {
size.setScale( scale );
}
if ( length != null ) {
size.setLength( length );
}
return size;
}
}
@ -3620,7 +3735,7 @@ public abstract class Dialect implements ConversionContext {
//most databases support a datetime format
//copied from Oracle's to_char() function,
//with some minor variation
return OracleDialect.datetimeFormat( format, true ).result();
return OracleDialect.datetimeFormat( format, true, false ).result();
}
/**

View File

@ -61,7 +61,6 @@ import java.util.regex.Pattern;
import javax.persistence.TemporalType;
import static org.hibernate.query.CastType.*;
import static org.hibernate.type.descriptor.DateTimeUtils.formatAsTimestampWithMillis;
/**
@ -219,24 +218,49 @@ public class FirebirdDialect extends Dialect {
*/
@Override
public String castPattern(CastType from, CastType to) {
if ( to==BOOLEAN
&& (from==LONG || from==INTEGER)) {
return "(0<>?1)";
String result;
switch ( to ) {
case FLOAT:
return "cast(double(?1) as real)";
case DOUBLE:
return "double(?1)";
case INTEGER:
case LONG:
result = BooleanDecoder.toInteger( from );
if ( result != null ) {
return result;
}
if ( getVersion() < 300 ) {
if ( to==BOOLEAN && from==STRING ) {
// return "iif(lower(?1) similar to 't|f|true|false', lower(?1) like 't%', null)";
return "decode(lower(?1),'t',1,'f',0,'true',1,'false',0)";
break;
case BOOLEAN:
result = BooleanDecoder.toBoolean( from );
if ( result != null ) {
return result;
}
if ( to==STRING && from==BOOLEAN ) {
return "trim(decode(?1,0,'false','true'))";
break;
case INTEGER_BOOLEAN:
result = BooleanDecoder.toIntegerBoolean( from );
if ( result != null ) {
return result;
}
break;
case YN_BOOLEAN:
result = BooleanDecoder.toYesNoBoolean( from );
if ( result != null ) {
return result;
}
else {
if ( from==BOOLEAN
&& (to==LONG || to==INTEGER)) {
return "decode(?1,false,0,true,1)";
break;
case TF_BOOLEAN:
result = BooleanDecoder.toTrueFalseBoolean( from );
if ( result != null ) {
return result;
}
break;
case STRING:
result = BooleanDecoder.toString( from );
if ( result != null ) {
return result;
}
break;
}
return super.castPattern( from, to );
}

View File

@ -65,9 +65,6 @@ import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
import org.jboss.logging.Logger;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.query.CastType.BOOLEAN;
import static org.hibernate.query.CastType.INTEGER;
import static org.hibernate.query.CastType.LONG;
/**
* An SQL dialect compatible with HyperSQL (HSQLDB) version 1.8 and above.
@ -237,12 +234,47 @@ public class HSQLDialect extends Dialect {
}
};
}
@Override
public String castPattern(CastType from, CastType to) {
if ( from== BOOLEAN
&& (to== INTEGER || to== LONG)) {
return "casewhen(?1,1,0)";
String result;
switch ( to ) {
case INTEGER:
case LONG:
result = BooleanDecoder.toInteger( from );
if ( result != null ) {
return result;
}
break;
case BOOLEAN:
result = BooleanDecoder.toBoolean( from );
if ( result != null ) {
return result;
}
break;
case INTEGER_BOOLEAN:
result = BooleanDecoder.toIntegerBoolean( from );
if ( result != null ) {
return result;
}
break;
case YN_BOOLEAN:
result = BooleanDecoder.toYesNoBoolean( from );
if ( result != null ) {
return result;
}
break;
case TF_BOOLEAN:
result = BooleanDecoder.toTrueFalseBoolean( from );
if ( result != null ) {
return result;
}
break;
case STRING:
result = BooleanDecoder.toString( from );
if ( result != null ) {
return result;
}
break;
}
return super.castPattern( from, to );
}
@ -647,7 +679,7 @@ public class HSQLDialect extends Dialect {
@Override
public String translateDatetimeFormat(String format) {
return OracleDialect.datetimeFormat(format, false)
return OracleDialect.datetimeFormat( format, false, false )
.replace("SSSSSS", "FF")
.replace("SSSSS", "FF")
.replace("SSSS", "FF")

View File

@ -8,6 +8,7 @@ package org.hibernate.dialect;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.IndividualLeastGreatestEmulation;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.InformixIdentityColumnSupport;
import org.hibernate.dialect.pagination.FirstLimitHandler;
@ -163,6 +164,9 @@ public class InformixDialect extends Dialect {
).setArgumentListSignature("(pattern, string[, start])");
//coalesce() and nullif() both supported since Informix 12
queryEngine.getSqmFunctionRegistry().register( "least", new IndividualLeastGreatestEmulation( true ) );
queryEngine.getSqmFunctionRegistry().register( "greatest", new IndividualLeastGreatestEmulation( false ) );
}
@Override

View File

@ -16,7 +16,6 @@ import org.hibernate.dialect.sequence.MimerSequenceSupport;
import org.hibernate.dialect.sequence.SequenceSupport;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.CastType;
import org.hibernate.query.SemanticException;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
@ -32,8 +31,6 @@ import java.sql.Types;
import javax.persistence.TemporalType;
import static org.hibernate.query.CastType.BOOLEAN;
/**
* A dialect for Mimer SQL 11.
*
@ -124,34 +121,6 @@ public class MimerSQLDialect extends Dialect {
};
}
/**
* Mimer does have a real {@link java.sql.Types#BOOLEAN}
* type, but it doesn't know how to cast to it.
*/
@Override
public String castPattern(CastType from, CastType to) {
switch (to) {
case BOOLEAN:
switch (from) {
case STRING:
// return "case when regexp_match(lower(?1), '^(t|f|true|false)$') then lower(?1) like 't%' end";
// return "case when lower(?1)in('t','true') then true when lower(?1)in('f','false') then false end";
return "case when ?1 in('t','true','T','TRUE') then true when ?1 in('f','false','F','FALSE') then false end";
case LONG:
case INTEGER:
return "(?1<>0)";
}
break;
case INTEGER:
case LONG:
if (from == BOOLEAN) {
return "case ?1 when false then 0 when true then 1 end";
}
break;
}
return super.castPattern(from, to);
}
@Override
public String currentTimestamp() {
return "localtimestamp";

View File

@ -22,6 +22,7 @@ import org.hibernate.dialect.sequence.NoSequenceSupport;
import org.hibernate.dialect.sequence.SequenceSupport;
import org.hibernate.dialect.unique.MySQLUniqueDelegate;
import org.hibernate.dialect.unique.UniqueDelegate;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.exception.LockAcquisitionException;
@ -33,7 +34,6 @@ import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.SqlExpressable;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.query.CastType;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
@ -47,6 +47,8 @@ import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import java.sql.CallableStatement;
import java.sql.ResultSet;
@ -56,7 +58,6 @@ import java.sql.Types;
import javax.persistence.TemporalType;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.query.CastType.BOOLEAN;
/**
* An SQL dialect for MySQL (prior to 5.x).
@ -68,6 +69,7 @@ public class MySQLDialect extends Dialect {
private final UniqueDelegate uniqueDelegate;
private final MySQLStorageEngine storageEngine;
private final int version;
private final SizeStrategy sizeStrategy;
public MySQLDialect(DialectResolutionInfo info) {
this( info.getDatabaseMajorVersion() * 100 + info.getDatabaseMinorVersion() * 10 );
@ -88,10 +90,10 @@ public class MySQLDialect extends Dialect {
if (storageEngine == null) {
this.storageEngine = getDefaultMySQLStorageEngine();
}
else if( "innodb".equals( storageEngine.toLowerCase() ) ) {
else if( "innodb".equalsIgnoreCase( storageEngine ) ) {
this.storageEngine = InnoDBStorageEngine.INSTANCE;
}
else if( "myisam".equals( storageEngine.toLowerCase() ) ) {
else if( "myisam".equalsIgnoreCase( storageEngine ) ) {
this.storageEngine = MyISAMStorageEngine.INSTANCE;
}
else {
@ -163,6 +165,25 @@ public class MySQLDialect extends Dialect {
getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE );
uniqueDelegate = new MySQLUniqueDelegate( this );
sizeStrategy = new SizeStrategyImpl() {
@Override
public Size resolveSize(
SqlTypeDescriptor sqlType,
JavaTypeDescriptor javaType,
Integer precision,
Integer scale,
Long length) {
final int jdbcTypeCode = sqlType.getSqlType();
switch ( jdbcTypeCode ) {
case Types.BIT:
// MySQL allows BIT with a length up to 64
if ( length != null ) {
return Size.length( Math.min( Math.max( length, 1 ), 64 ) );
}
}
return super.resolveSize( sqlType, javaType, precision, scale, length );
}
};
}
@Override
@ -174,6 +195,11 @@ public class MySQLDialect extends Dialect {
return version;
}
@Override
public SizeStrategy getSizeStrategy() {
return sizeStrategy;
}
@Override
public long getDefaultLobLength() {
//max length for mediumblob or mediumtext
@ -334,31 +360,6 @@ public class MySQLDialect extends Dialect {
}
}
/**
* MySQL doesn't have a real {@link java.sql.Types#BOOLEAN}
* type, so...
*/
@Override
public String castPattern(CastType from, CastType to) {
switch (to) {
case BOOLEAN:
switch (from) {
case STRING:
// return "if(?1 rlike '^(t|f|true|false)$', ?1 like 't%', null)";
return "if(lower(?1) in('t','f','true','false'), ?1 like 't%', null)";
case LONG:
case INTEGER:
return "(?1<>0)";
}
case STRING:
if (from == BOOLEAN) {
return "if(?1,'true','false')";
}
default:
return super.castPattern(from, to);
}
}
@Override
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType) {
switch (unit) {
@ -717,11 +718,14 @@ public class MySQLDialect extends Dialect {
return new LockAcquisitionException( message, sqlException, sql );
}
switch ( JdbcExceptionHelper.extractSqlState( sqlException ) ) {
final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
if ( sqlState != null ) {
switch ( sqlState ) {
case "41000":
return new LockTimeoutException(message, sqlException, sql);
return new LockTimeoutException( message, sqlException, sql );
case "40001":
return new LockAcquisitionException(message, sqlException, sql);
return new LockAcquisitionException( message, sqlException, sql );
}
}
return null;

View File

@ -70,7 +70,6 @@ import java.util.regex.Pattern;
import javax.persistence.TemporalType;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.query.CastType.STRING;
import static org.hibernate.query.TemporalUnit.*;
/**
@ -234,19 +233,40 @@ public class OracleDialect extends Dialect {
*/
@Override
public String castPattern(CastType from, CastType to) {
switch (to) {
case BOOLEAN:
switch (from) {
case STRING:
return "decode(lower(?1),'t',1,'f',0,'true',1,'false',0)";
case LONG:
String result;
switch ( to ) {
case INTEGER:
return "abs(sign(?1))";
case LONG:
result = BooleanDecoder.toInteger( from );
if ( result != null ) {
return result;
}
case STRING:
switch (from) {
break;
case INTEGER_BOOLEAN:
result = BooleanDecoder.toIntegerBoolean( from );
if ( result != null ) {
return result;
}
break;
case YN_BOOLEAN:
result = BooleanDecoder.toYesNoBoolean( from );
if ( result != null ) {
return result;
}
break;
case BOOLEAN:
return "decode(?1,0,'false','true')";
case TF_BOOLEAN:
result = BooleanDecoder.toTrueFalseBoolean( from );
if ( result != null ) {
return result;
}
break;
case STRING:
switch ( from ) {
case INTEGER_BOOLEAN:
case TF_BOOLEAN:
case YN_BOOLEAN:
return BooleanDecoder.toString( from );
case DATE:
return "to_char(?1,'YYYY-MM-DD')";
case TIME:
@ -258,29 +278,34 @@ public class OracleDialect extends Dialect {
case ZONE_TIMESTAMP:
return "to_char(?1,'YYYY-MM-DD HH24:MI:SS.FF9 TZR')";
}
break;
case DATE:
if (from == STRING) {
if ( from == CastType.STRING ) {
return "to_date(?1,'YYYY-MM-DD')";
}
break;
case TIME:
if (from == STRING) {
if ( from == CastType.STRING ) {
return "to_date(?1,'HH24:MI:SS')";
}
break;
case TIMESTAMP:
if (from == STRING) {
if ( from == CastType.STRING ) {
return "to_timestamp(?1,'YYYY-MM-DD HH24:MI:SS.FF9')";
}
break;
case OFFSET_TIMESTAMP:
if (from == STRING) {
if ( from == CastType.STRING ) {
return "to_timestamp_tz(?1,'YYYY-MM-DD HH24:MI:SS.FF9TZH:TZM')";
}
break;
case ZONE_TIMESTAMP:
if (from == STRING) {
if ( from == CastType.STRING ) {
return "to_timestamp_tz(?1,'YYYY-MM-DD HH24:MI:SS.FF9 TZR')";
}
default:
return super.castPattern(from, to);
break;
}
return super.castPattern(from, to);
}
/**
@ -315,6 +340,20 @@ public class OracleDialect extends Dialect {
return "to_number(to_char(?2,'DDD'))";
case WEEK:
return "to_number(to_char(?2,'IW'))"; //the ISO week number
case WEEK_OF_YEAR:
return "to_number(to_char(?2,'WW'))";
// Oracle doesn't support extracting the quarter
case QUARTER:
return "to_number(to_char(?2,'Q'))";
// Oracle can't extract time parts from a date column, so we need to cast to timestamp
// This is because Oracle treats date as ANSI SQL date which has no time part
// Also see https://docs.oracle.com/cd/B28359_01/server.111/b28286/functions052.htm#SQLRF00639
case HOUR:
return "to_number(to_char(?2,'HH24'))";
case MINUTE:
return "to_number(to_char(?2,'MI'))";
case SECOND:
return "to_number(to_char(?2,'SS'))";
default:
return super.extractPattern(unit);
}
@ -855,7 +894,11 @@ public class OracleDialect extends Dialect {
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableStrategy(
new IdTable( rootEntityDescriptor, name -> name.length() > 30 ? name.substring( 0, 30 ) : name, this ),
new IdTable(
rootEntityDescriptor,
name -> "HT_" + ( name.length() > 27 ? name.substring( 0, 27 ) : name ),
this
),
() -> new TempIdTableExporter( false, this::getTypeName ) {
@Override
protected String getCreateOptions() {
@ -1050,8 +1093,16 @@ public class OracleDialect extends Dialect {
return getWriteLockString( aliases, timeout );
}
public static Replacer datetimeFormat(String format, boolean useFm) {
@Override
public String translateDatetimeFormat(String format) {
// Unlike other databases, Oracle requires an explicit reset for the fm modifier,
// otherwise all following pattern variables trim zeros
return datetimeFormat( format, true, true ).result();
}
public static Replacer datetimeFormat(String format, boolean useFm, boolean resetFm) {
String fm = useFm ? "fm" : "";
String fmReset = resetFm ? fm : "";
return new Replacer( format, "'", "\"" )
//era
.replace("GG", "AD")
@ -1059,42 +1110,42 @@ public class OracleDialect extends Dialect {
//year
.replace("yyyy", "YYYY")
.replace("yyy", fm + "YYYY")
.replace("yyy", fm + "YYYY" + fmReset)
.replace("yy", "YY")
.replace("y", fm + "YYYY")
.replace("y", fm + "YYYY" + fmReset)
//month of year
.replace("MMMM", fm + "Month")
.replace("MMMM", fm + "Month" + fmReset)
.replace("MMM", "Mon")
.replace("MM", "MM")
.replace("M", fm + "MM")
.replace("M", fm + "MM" + fmReset)
//week of year
.replace("ww", "IW")
.replace("w", fm + "IW")
.replace("w", fm + "IW" + fmReset)
//year for week
.replace("YYYY", "IYYY")
.replace("YYY", fm + "IYYY")
.replace("YYY", fm + "IYYY" + fmReset)
.replace("YY", "IY")
.replace("Y", fm + "IYYY")
.replace("Y", fm + "IYYY" + fmReset)
//week of month
.replace("W", "W")
//day of week
.replace("EEEE", fm + "Day")
.replace("EEEE", fm + "Day" + fmReset)
.replace("EEE", "Dy")
.replace("ee", "D")
.replace("e", fm + "D")
.replace("e", fm + "D" + fmReset)
//day of month
.replace("dd", "DD")
.replace("d", fm + "DD")
.replace("d", fm + "DD" + fmReset)
//day of year
.replace("DDD", "DDD")
.replace("DD", fm + "DDD")
.replace("D", fm + "DDD")
.replace("DD", fm + "DDD" + fmReset)
.replace("D", fm + "DDD" + fmReset)
//am pm
.replace("aa", "AM")
@ -1103,16 +1154,16 @@ public class OracleDialect extends Dialect {
//hour
.replace("hh", "HH12")
.replace("HH", "HH24")
.replace("h", fm + "HH12")
.replace("H", fm + "HH24")
.replace("h", fm + "HH12" + fmReset)
.replace("H", fm + "HH24" + fmReset)
//minute
.replace("mm", "MI")
.replace("m", fm + "MI")
.replace("m", fm + "MI" + fmReset)
//second
.replace("ss", "SS")
.replace("s", fm + "SS")
.replace("s", fm + "SS" + fmReset)
//fractional seconds
.replace("SSSSSS", "FF6")

View File

@ -6,10 +6,14 @@
*/
package org.hibernate.dialect;
import org.hibernate.FetchClauseType;
import java.util.List;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.insert.Values;
import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec;
@ -34,6 +38,30 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
);
}
@Override
protected void visitValuesList(List<Values> valuesList) {
if ( valuesList.size() < 2 ) {
super.visitValuesList( valuesList );
}
else {
// Oracle doesn't support a multi-values insert
// So we render a select union emulation instead
String separator = "";
final Stack<Clause> clauseStack = getClauseStack();
try {
clauseStack.push( Clause.VALUES );
for ( Values values : valuesList ) {
appendSql( separator );
renderExpressionsAsSubquery( values.getExpressions() );
separator = " union all ";
}
}
finally {
clauseStack.pop();
}
}
}
@Override
public void visitQueryGroup(QueryGroup queryGroup) {
if ( shouldEmulateFetchClause( queryGroup ) ) {

View File

@ -669,7 +669,7 @@ public class PostgreSQLDialect extends Dialect {
}
public Replacer datetimeFormat(String format) {
return OracleDialect.datetimeFormat(format, true)
return OracleDialect.datetimeFormat( format, true, false )
.replace("SSSSSS", "US")
.replace("SSSSS", "US")
.replace("SSSS", "US")

View File

@ -343,7 +343,7 @@ public class RDMSOS2200Dialect extends Dialect {
@Override
public String translateDatetimeFormat(String format) {
return OracleDialect.datetimeFormat( format, true ) //Does it really support FM?
return OracleDialect.datetimeFormat( format, true, false ) //Does it really support FM?
.replace("SSSSSS", "MLS")
.replace("SSSSS", "MLS")
.replace("SSSS", "MLS")

View File

@ -8,6 +8,7 @@ package org.hibernate.dialect;
import org.hibernate.*;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.SQLServerFormatFunction;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.SQLServerIdentityColumnSupport;
import org.hibernate.dialect.pagination.LimitHandler;
@ -22,6 +23,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.exception.LockTimeoutException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.query.CastType;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.sql.ast.SqlAstTranslator;
@ -146,7 +148,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
}
if ( getVersion() >= 11 ) {
CommonFunctionFactory.format_format( queryEngine );
queryEngine.getSqmFunctionRegistry().register( "format", new SQLServerFormatFunction( this ) );
//actually translate() was added in 2017 but
//it's not worth adding a new dialect for that!
@ -192,6 +194,22 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
};
}
@Override
public String castPattern(CastType from, CastType to) {
if ( to == CastType.STRING ) {
switch ( from ) {
case TIMESTAMP:
// SQL Server uses yyyy-MM-dd HH:mm:ss.nnnnnnn by default when doing a cast, but only need second precision
return "format(?1,'yyyy-MM-dd HH:mm:ss')";
case TIME:
// SQL Server uses HH:mm:ss.nnnnnnn by default when doing a cast, but only need second precision
// SQL Server requires quoting of ':' in time formats and the use of 'hh' instead of 'HH'
return "format(?1,'hh\\:mm\\:ss')";
}
}
return super.castPattern( from, to );
}
@Override
public String currentTimestamp() {
return "sysdatetime()";
@ -199,7 +217,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
@Override
public String currentTime() {
return currentTimestamp();
return "convert(time, getdate())";
}
@Override
@ -502,16 +520,25 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
@Override
public String extractPattern(TemporalUnit unit) {
switch (unit) {
case TIMEZONE_HOUR:
return "(datepart(tz,?2)/60)";
case TIMEZONE_MINUTE:
return "(datepart(tz,?2)%60)";
//currently Dialect.extract() doesn't need
//to handle NANOSECOND (might change that?)
// case NANOSECOND:
// //this should evaluate to a bigint type
// return "(datepart(second,?2,?3)*1000000000+datepart(nanosecond,?2,?3))";
// return "(datepart(second,?2)*1000000000+datepart(nanosecond,?2))";
case SECOND:
//this should evaluate to a floating point type
return "(datepart(second,?2,?3)+datepart(nanosecond,?2,?3)/1e9)";
return "(datepart(second,?2)+datepart(nanosecond,?2)/1e9)";
case WEEK:
// Thanks https://www.sqlservercentral.com/articles/a-simple-formula-to-calculate-the-iso-week-number
if ( getVersion() < 10 ) {
return "(DATEPART(dy,DATEADD(dd,DATEDIFF(dd,'17530101',?2)/7*7,'17530104'))+6)/7)";
}
default:
return "datepart(?1,?2,?3)";
return "datepart(?1,?2)";
}
}
@ -565,6 +592,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
switch ( unit ) {
//the ISO week number (behavior of "week" depends on a system property)
case WEEK: return "isowk";
case OFFSET: return "tz";
default: return super.translateExtractField(unit);
}
}

View File

@ -45,6 +45,20 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
hasLimit = queryPart.getFetchClauseExpression() != null;
hasOffset = queryPart.getOffsetClauseExpression() != null;
}
if ( queryPart instanceof QueryGroup ) {
// We can't use TOP for set operations
if ( hasOffset || hasLimit ) {
if ( version < 11 || !isRowsOnlyFetchClauseType( queryPart ) ) {
return OffsetFetchClauseMode.EMULATED;
}
else {
return OffsetFetchClauseMode.STANDARD;
}
}
return null;
}
else {
if ( version < 9 || !hasOffset ) {
return hasLimit ? OffsetFetchClauseMode.TOP_ONLY : null;
}
@ -55,6 +69,15 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
return OffsetFetchClauseMode.STANDARD;
}
}
}
@Override
protected boolean supportsSimpleQueryGrouping() {
// SQL Server is quite strict i.e. it requires `select .. union all select * from (select ...)`
// rather than `select .. union all (select ...)` because parenthesis followed by select
// is always treated as a subquery, which is not supported in a set operation
return false;
}
protected boolean shouldEmulateFetchClause(QueryPart queryPart) {
// Check if current query part is already row numbering to avoid infinite recursion

View File

@ -7,6 +7,7 @@
package org.hibernate.dialect;
import org.hibernate.LockOptions;
import org.hibernate.dialect.function.QuantifiedLeastGreatestEmulation;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.TopLimitHandler;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
@ -16,6 +17,7 @@ import org.hibernate.exception.LockTimeoutException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.query.TrimSpec;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.sql.ForUpdateFragment;
import org.hibernate.sql.JoinFragment;
import org.hibernate.sql.Sybase11JoinFragment;
@ -340,7 +342,14 @@ public class SybaseASEDialect extends SybaseDialect {
registerKeyword( "xmlvalidate" );
}
// Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public void initializeFunctionRegistry(QueryEngine queryEngine) {
super.initializeFunctionRegistry( queryEngine );
queryEngine.getSqmFunctionRegistry().register( "least", new QuantifiedLeastGreatestEmulation( true ) );
queryEngine.getSqmFunctionRegistry().register( "greatest", new QuantifiedLeastGreatestEmulation( false ) );
}
// Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override

View File

@ -42,11 +42,11 @@ public class CastFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
public void render(SqlAppender sqlAppender, List<SqlAstNode> arguments, SqlAstWalker walker) {
final Expression source = (Expression) arguments.get( 0 );
final JdbcMapping sourceMapping = ( (SqlExpressable) source.getExpressionType() ).getJdbcMapping();
final CastType sourceType = CastType.from( sourceMapping );
final CastType sourceType = sourceMapping.getCastType();
final CastTarget castTarget = (CastTarget) arguments.get( 1 );
final JdbcMapping targetJdbcMapping = castTarget.getExpressionType().getJdbcMapping();
final CastType targetType = CastType.from( targetJdbcMapping );
final CastType targetType = targetJdbcMapping.getCastType();
String cast = dialect.castPattern( sourceType, targetType );

View File

@ -1254,7 +1254,7 @@ public class CommonFunctionFactory {
.registerBinaryTernaryPattern(
"substring",
StandardBasicTypes.STRING,
"substring(?1,?2,len(?1)-?2)",
"substring(?1,?2,len(?1)-?2+1)",
"substring(?1,?2,?3)"
)
.setArgumentListSignature("(string{ from|,} start[{ for|,} length])");

View File

@ -50,7 +50,7 @@ public class DB2FormatEmulation
final Format format = (Format) arguments.get(1);
sqlAppender.appendSql("(");
String[] bits = OracleDialect.datetimeFormat( format.getFormat(), false ).result().split("\"");
String[] bits = OracleDialect.datetimeFormat( format.getFormat(), false, false ).result().split("\"");
boolean first = true;
for ( int i=0; i<bits.length; i++ ) {
String bit = bits[i];

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.dialect.function;
import java.util.List;
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
/**
*
* @author Christian Beikov
*/
public class IndividualLeastGreatestEmulation
extends AbstractSqmSelfRenderingFunctionDescriptor {
private final String operator;
public IndividualLeastGreatestEmulation(boolean least) {
super(
least ? "least" : "greatest",
StandardArgumentsValidators.min( 2 ),
StandardFunctionReturnTypeResolvers.useFirstNonNull()
);
this.operator = least ? "<=" : ">=";
}
@Override
public void render(
SqlAppender sqlAppender,
List<SqlAstNode> arguments,
SqlAstWalker walker) {
final int numberOfArguments = arguments.size();
if ( numberOfArguments > 1 ) {
final int lastArgument = numberOfArguments - 1;
sqlAppender.appendSql( "case" );
for ( int i = 0; i < lastArgument; i++ ) {
sqlAppender.appendSql( " when " );
String separator = "";
for ( int j = i + 1; j < numberOfArguments; j++ ) {
sqlAppender.appendSql( separator );
arguments.get( i ).accept( walker );
sqlAppender.appendSql( operator );
arguments.get( j ).accept( walker );
separator = " and ";
}
sqlAppender.appendSql( " then " );
arguments.get( i ).accept( walker );
}
sqlAppender.appendSql( " else " );
arguments.get( lastArgument ).accept( walker );
sqlAppender.appendSql( " end" );
}
else {
arguments.get( 0 ).accept( walker );
}
}
@Override
public String getArgumentListSignature() {
return "(arg0[, arg1[, ...]])";
}
}

View File

@ -8,7 +8,9 @@ package org.hibernate.dialect.function;
import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType;
import org.hibernate.query.BinaryArithmeticOperator;
import org.hibernate.query.ComparisonOperator;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.SqmExpressable;
import org.hibernate.query.sqm.function.AbstractSqmFunctionDescriptor;
import org.hibernate.query.sqm.function.SelfRenderingSqmFunction;
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
@ -16,14 +18,18 @@ import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic;
import org.hibernate.query.sqm.tree.expression.SqmCaseSearched;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate;
import org.hibernate.type.BasicType;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.spi.TypeConfiguration;
import java.util.List;
import javax.persistence.criteria.Expression;
import static java.util.Arrays.asList;
/**
@ -32,12 +38,15 @@ import static java.util.Arrays.asList;
public class InsertSubstringOverlayEmulation
extends AbstractSqmFunctionDescriptor {
public InsertSubstringOverlayEmulation() {
private final boolean strictSubstring;
public InsertSubstringOverlayEmulation(boolean strictSubstring) {
super(
"overlay",
StandardArgumentsValidators.between( 3, 4 ),
StandardFunctionReturnTypeResolvers.invariant( StandardBasicTypes.STRING )
);
this.strictSubstring = strictSubstring;
}
@Override
@ -66,6 +75,7 @@ public class InsertSubstringOverlayEmulation
);
}
else {
SqmFunctionDescriptor lengthFunction = queryEngine.getSqmFunctionRegistry().findFunctionDescriptor("length");
SqmFunctionDescriptor substring = queryEngine.getSqmFunctionRegistry().findFunctionDescriptor("substring");
SqmFunctionDescriptor concat = queryEngine.getSqmFunctionRegistry().findFunctionDescriptor("concat");
SqmLiteral<Integer> one = new SqmLiteral<>( 1, intType, queryEngine.getCriteriaBuilder() );
@ -83,6 +93,30 @@ public class InsertSubstringOverlayEmulation
intType,
queryEngine.getCriteriaBuilder()
);
SqmExpressable<Object> stringType = (SqmExpressable<Object>) impliedResultType;
SqmTypedNode<?> restString = substring.generateSqmExpression(
asList( string, startPlusLength ),
impliedResultType,
queryEngine,
typeConfiguration
);
if ( strictSubstring ) {
restString = (SqmTypedNode<?>) new SqmCaseSearched<>( stringType, start.nodeBuilder() )
.when(
new SqmComparisonPredicate(
startPlusLength,
ComparisonOperator.GREATER_THAN,
lengthFunction.generateSqmExpression(
asList( string ),
intType,
queryEngine,
typeConfiguration
),
string.nodeBuilder()
),
(Expression<?>) new SqmLiteral<>( "", stringType, string.nodeBuilder() )
).otherwise( (Expression<?>) restString );
}
return concat.generateSqmExpression(
asList(
substring.generateSqmExpression(
@ -92,12 +126,7 @@ public class InsertSubstringOverlayEmulation
typeConfiguration
),
replacement,
substring.generateSqmExpression(
asList( string, startPlusLength ),
impliedResultType,
queryEngine,
typeConfiguration
)
restString
),
impliedResultType,
queryEngine,

View File

@ -0,0 +1,74 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.dialect.function;
import java.util.List;
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.type.StandardBasicTypes;
/**
*
* @author Christian Beikov
*/
public class QuantifiedLeastGreatestEmulation
extends AbstractSqmSelfRenderingFunctionDescriptor {
private final String operator;
public QuantifiedLeastGreatestEmulation(boolean least) {
super(
least ? "least" : "greatest",
StandardArgumentsValidators.min( 2 ),
StandardFunctionReturnTypeResolvers.useFirstNonNull()
);
this.operator = least ? "<=" : ">=";
}
@Override
public void render(
SqlAppender sqlAppender,
List<SqlAstNode> arguments,
SqlAstWalker walker) {
final int numberOfArguments = arguments.size();
if ( numberOfArguments > 1 ) {
final int lastArgument = numberOfArguments - 1;
sqlAppender.appendSql( "case" );
for ( int i = 0; i < lastArgument; i++ ) {
sqlAppender.appendSql( " when " );
arguments.get( i ).accept( walker );
sqlAppender.appendSql( operator );
sqlAppender.appendSql( " all(" );
String separator = "";
for ( int j = i + 1; j < numberOfArguments; j++ ) {
sqlAppender.appendSql( separator );
arguments.get( j ).accept( walker );
separator = ", ";
}
sqlAppender.appendSql( ") then " );
arguments.get( i ).accept( walker );
}
sqlAppender.appendSql( " else " );
arguments.get( lastArgument ).accept( walker );
sqlAppender.appendSql( " end" );
}
else {
arguments.get( 0 ).accept( walker );
}
}
@Override
public String getArgumentListSignature() {
return "(arg0[, arg1[, ...]])";
}
}

View File

@ -0,0 +1,69 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.dialect.function;
import java.util.List;
import javax.persistence.TemporalType;
import org.hibernate.dialect.SQLServerDialect;
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Format;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.spi.TypeConfiguration;
/**
* SQL Server behaves strangely when the first argument to format is of the type time, so we cast to datetime.
*
* @author Christian Beikov
*/
public class SQLServerFormatFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
private final SQLServerDialect dialect;
public SQLServerFormatFunction(SQLServerDialect dialect) {
super(
"format",
StandardArgumentsValidators.exactly( 2 ),
StandardFunctionReturnTypeResolvers.invariant( StandardBasicTypes.STRING )
);
this.dialect = dialect;
}
@Override
public void render(
SqlAppender sqlAppender,
List<SqlAstNode> arguments,
SqlAstWalker walker) {
final Expression datetime = (Expression) arguments.get(0);
final boolean isTime = TypeConfiguration.getSqlTemporalType( datetime.getExpressionType() ) == TemporalType.TIME;
final Format format = (Format) arguments.get(1);
sqlAppender.appendSql("format(");
if ( isTime ) {
sqlAppender.appendSql("cast(");
datetime.accept( walker );
sqlAppender.appendSql(" as datetime)");
}
else {
datetime.accept( walker );
}
sqlAppender.appendSql(",'");
sqlAppender.appendSql( dialect.translateDatetimeFormat( format.getFormat() ) );
sqlAppender.appendSql("')");
}
@Override
public String getArgumentListSignature() {
return "(datetime as pattern)";
}
}

View File

@ -240,11 +240,8 @@ public class Column implements Selectable, Serializable, Cloneable {
public String getSqlType(Dialect dialect, Mapping mapping) throws HibernateException {
if ( sqlType == null ) {
final Size defaultSize = getColumnDefaultSize( dialect, mapping );
final Size size = getColumnSize( defaultSize );
try {
sqlType = dialect.getTypeName( getSqlTypeCode( mapping ), size );
sqlType = dialect.getTypeName( getSqlTypeCode( mapping ), getColumnSize( dialect, mapping ) );
}
catch (HibernateException cause) {
throw new HibernateException(
@ -261,15 +258,7 @@ public class Column implements Selectable, Serializable, Cloneable {
return sqlType;
}
private Size getColumnSize(Size defaultSize) {
final Integer columnPrecision = precision != null ? precision : defaultSize.getPrecision();
final Integer columnScale = scale != null ? scale : defaultSize.getScale();
final Long columnLength = length != null ? length : defaultSize.getLength();
return new Size( columnPrecision, columnScale, columnLength, null );
}
private Size getColumnDefaultSize(Dialect dialect, Mapping mapping) {
private Size getColumnSize(Dialect dialect, Mapping mapping) {
Type type = getValue().getType();
if ( type instanceof EntityType ) {
@ -278,9 +267,12 @@ public class Column implements Selectable, Serializable, Cloneable {
if ( type instanceof ComponentType ) {
type = getTypeForComponentValue( mapping, type, getTypeIndex() );
}
return dialect.getDefaultSizeStrategy().resolveDefaultSize(
return dialect.getSizeStrategy().resolveSize(
( (JdbcMapping) type ).getSqlTypeDescriptor(),
( (JdbcMapping) type ).getJavaTypeDescriptor()
( (JdbcMapping) type ).getJavaTypeDescriptor(),
precision,
scale,
length
);
}

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.metamodel.mapping;
import org.hibernate.query.CastType;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
@ -29,6 +30,9 @@ public interface JdbcMapping extends MappingType {
*/
SqlTypeDescriptor getSqlTypeDescriptor();
default CastType getCastType() {
return getSqlTypeDescriptor().getCastType();
}
/**
* The strategy for extracting values of this expressable

View File

@ -8,16 +8,16 @@ package org.hibernate.metamodel.mapping.internal;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.SortOrder;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.SelectionMapping;
import org.hibernate.metamodel.mapping.ordering.ast.DomainPath;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.select.QuerySpec;
@ -37,7 +37,6 @@ public abstract class AbstractDomainPath implements DomainPath {
String modelPartName,
SortOrder sortOrder,
SqlAstCreationState creationState) {
final SqlAstCreationContext creationContext = creationState.getCreationContext();
apply(
getReferenceModelPart(),
ast,
@ -45,9 +44,7 @@ public abstract class AbstractDomainPath implements DomainPath {
collation,
modelPartName,
sortOrder,
creationState,
creationContext.getSessionFactory(),
creationState.getSqlExpressionResolver()
creationState
);
}
@ -58,9 +55,7 @@ public abstract class AbstractDomainPath implements DomainPath {
String collation,
String modelPartName,
SortOrder sortOrder,
SqlAstCreationState creationState,
SessionFactoryImplementor sessionFactory,
SqlExpressionResolver sqlExprResolver) {
SqlAstCreationState creationState) {
if ( referenceModelPart instanceof BasicValuedModelPart ) {
addSortSpecification(
(BasicValuedModelPart) referenceModelPart,
@ -86,9 +81,7 @@ public abstract class AbstractDomainPath implements DomainPath {
collation,
modelPartName,
sortOrder,
creationState,
sessionFactory,
sqlExprResolver
creationState
);
}
else if ( referenceModelPart instanceof EmbeddableValuedModelPart ) {
@ -99,9 +92,7 @@ public abstract class AbstractDomainPath implements DomainPath {
collation,
modelPartName,
sortOrder,
creationState,
sessionFactory,
sqlExprResolver
creationState
);
}
else {
@ -117,30 +108,18 @@ public abstract class AbstractDomainPath implements DomainPath {
String collation,
String modelPartName,
SortOrder sortOrder,
SqlAstCreationState creationState,
SessionFactoryImplementor sessionFactory,
SqlExpressionResolver sqlExprResolver) {
SqlAstCreationState creationState) {
if ( embeddableValuedModelPart.getFetchableName()
.equals( modelPartName ) || ELEMENT_TOKEN.equals( modelPartName ) ) {
embeddableValuedModelPart.forEachSelection(
(columnIndex, selection) -> {
final TableReference tableReference = tableGroup.resolveTableReference( selection.getContainingTableExpression() );
ast.addSortSpecification(
new SortSpecification(
sqlExprResolver.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey(
selection.getContainingTableExpression(),
selection.getSelectionExpression()
),
sqlAstProcessingState -> new ColumnReference(
tableReference,
addSortSpecification(
selection,
sessionFactory
)
),
ast,
tableGroup,
collation,
sortOrder
)
sortOrder,
creationState
);
}
);
@ -160,24 +139,33 @@ public abstract class AbstractDomainPath implements DomainPath {
}
private void addSortSpecification(
BasicValuedModelPart basicValuedPart,
SelectionMapping selection,
QuerySpec ast,
TableGroup tableGroup,
String collation,
SortOrder sortOrder,
SqlAstCreationState creationState) {
final TableReference tableReference = tableGroup.resolveTableReference( basicValuedPart.getContainingTableExpression() );
ast.addSortSpecification(
new SortSpecification(
new ColumnReference(
tableReference,
basicValuedPart,
creationState.getCreationContext().getSessionFactory()
final TableReference tableReference = tableGroup.resolveTableReference( selection.getContainingTableExpression() );
final Expression expression = creationState.getSqlExpressionResolver().resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey(
selection.getContainingTableExpression(),
selection.getSelectionExpression()
),
collation,
sortOrder
sqlAstProcessingState -> new ColumnReference(
tableReference,
selection,
creationState.getCreationContext().getSessionFactory()
)
);
// It makes no sense to order by an expression multiple times
// SQL Server even reports a query error in this case
if ( ast.hasSortSpecifications() ) {
for ( SortSpecification sortSpecification : ast.getSortSpecifications() ) {
if ( sortSpecification.getSortExpression() == expression ) {
return;
}
}
}
ast.addSortSpecification( new SortSpecification( expression, collation, sortOrder ) );
}
}

View File

@ -15,6 +15,7 @@ import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
@ -70,10 +71,7 @@ public class ColumnReference implements OrderingExpression, SequencePart {
tableReference = getTableReference( tableGroup );
final SqlExpressionResolver sqlExpressionResolver = creationState.getSqlExpressionResolver();
ast.addSortSpecification(
new SortSpecification(
sqlExpressionResolver.resolveSqlExpression(
final Expression expression = sqlExpressionResolver.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey( tableReference, columnExpression ),
sqlAstProcessingState -> new org.hibernate.sql.ast.tree.expression.ColumnReference(
tableReference,
@ -86,11 +84,18 @@ public class ColumnReference implements OrderingExpression, SequencePart {
null,
creationState.getCreationContext().getSessionFactory()
)
),
collation,
sortOrder
)
);
// It makes no sense to order by an expression multiple times
// SQL Server even reports a query error in this case
if ( ast.hasSortSpecifications() ) {
for ( SortSpecification sortSpecification : ast.getSortSpecifications() ) {
if ( sortSpecification.getSortExpression() == expression ) {
return;
}
}
}
ast.addSortSpecification( new SortSpecification( expression, collation, sortOrder ) );
}
TableReference getTableReference(TableGroup tableGroup) {

View File

@ -6,13 +6,6 @@
*/
package org.hibernate.query;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.query.sqm.SqmExpressable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.*;
/**
* Defines the set of basic types which should be
* accepted by the {@code cast()} function on every
@ -33,76 +26,25 @@ import java.time.*;
* @author Gavin King
*/
public enum CastType {
STRING(CastTypeKind.TEXT),
BOOLEAN(CastTypeKind.BOOLEAN),
INTEGER(CastTypeKind.NUMERIC), LONG(CastTypeKind.NUMERIC), FLOAT(CastTypeKind.NUMERIC), DOUBLE(CastTypeKind.NUMERIC), FIXED(CastTypeKind.NUMERIC),
DATE(CastTypeKind.TEMPORAL), TIME(CastTypeKind.TEMPORAL), TIMESTAMP(CastTypeKind.TEMPORAL),
OFFSET_TIMESTAMP(CastTypeKind.TEMPORAL), ZONE_TIMESTAMP(CastTypeKind.TEMPORAL),
NULL(null),
OTHER(null);
STRING,
BOOLEAN, INTEGER_BOOLEAN, YN_BOOLEAN, TF_BOOLEAN,
INTEGER, LONG, FLOAT, DOUBLE, FIXED,
DATE, TIME, TIMESTAMP,
OFFSET_TIMESTAMP, ZONE_TIMESTAMP,
NULL,
OTHER;
private final CastTypeKind kind;
CastType(CastTypeKind kind) {
this.kind = kind;
public boolean isNumeric() {
switch (this) {
case INTEGER:
case LONG:
case INTEGER_BOOLEAN:
case FLOAT:
case DOUBLE:
case FIXED:
return true;
default:
return false;
}
public CastTypeKind getKind() {
return kind;
}
public static CastType from(Class javaClass) {
if (String.class.equals(javaClass)) {
return STRING;
}
if (Boolean.class.equals(javaClass)
|| boolean.class.equals(javaClass)) {
return BOOLEAN;
}
if (Integer.class.equals(javaClass)
|| int.class.equals(javaClass)) {
return INTEGER;
}
if (Long.class.equals(javaClass)
|| long.class.equals(javaClass)) {
return LONG;
}
if (Float.class.equals(javaClass)
|| float.class.equals(javaClass)) {
return FLOAT;
}
if (Double.class.equals(javaClass)
|| double.class.equals(javaClass)) {
return DOUBLE;
}
if (BigInteger.class.equals(javaClass)) {
return FIXED;
}
if (BigDecimal.class.equals(javaClass)) {
return FIXED;
}
if (LocalDate.class.equals(javaClass)) {
return DATE;
}
if (LocalTime.class.equals(javaClass)) {
return TIME;
}
if (LocalDateTime.class.equals(javaClass)) {
return TIMESTAMP;
}
if (OffsetDateTime.class.equals(javaClass)) {
return OFFSET_TIMESTAMP;
}
if (ZonedDateTime.class.equals(javaClass)) {
return ZONE_TIMESTAMP;
}
return OTHER;
}
public static CastType from(JdbcMapping jdbcMapping) {
//TODO: I really don't like using the Java type
// here. Is there some way to base this
// off of the mapped SQL type?
return jdbcMapping == null ? NULL : from( jdbcMapping.getJavaTypeDescriptor().getJavaType() );
}
}

View File

@ -1,21 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.query;
/**
* The kind of type of a cast target.
*
* @see CastType
*
* @author Christian Beikov
*/
public enum CastTypeKind {
BOOLEAN,
NUMERIC,
TEMPORAL,
TEXT
}

View File

@ -317,7 +317,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
);
try {
visitQueryExpression( queryExpressionContext );
queryExpressionContext.accept( this );
}
finally {
processingStateStack.pop();
@ -347,7 +347,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
processingStateStack.push( processingState );
try {
visitQueryExpression( queryExpressionContext );
queryExpressionContext.accept( this );
final SqmCreationProcessingState stateFieldsProcessingState = new SqmCreationProcessingStateImpl(
insertStatement,
@ -480,20 +480,28 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
// Query spec
@Override
public SqmQueryPart visitQueryExpression(HqlParser.QueryExpressionContext ctx) {
final SqmQueryPart queryPart = (SqmQueryPart) ctx.queryGroup().accept( this );
visitQueryOrder( queryPart, ctx.queryOrder() );
public SqmQueryPart visitSimpleQueryGroup(HqlParser.SimpleQueryGroupContext ctx) {
return (SqmQueryPart) ctx.simpleQueryExpression().accept( this );
}
@Override
public SqmQueryPart visitQuerySpecExpression(HqlParser.QuerySpecExpressionContext ctx) {
final List<ParseTree> children = ctx.children;
final SqmQueryPart queryPart = visitQuerySpec( (HqlParser.QuerySpecContext) children.get( 0 ) );
if ( children.size() > 1 ) {
visitQueryOrder( queryPart, (HqlParser.QueryOrderContext) children.get( 1 ) );
}
return queryPart;
}
@Override
public SqmQueryPart visitQuerySpecQueryGroup(HqlParser.QuerySpecQueryGroupContext ctx) {
return visitQuerySpec( ctx.querySpec() );
public SqmQueryPart visitNestedQueryExpression(HqlParser.NestedQueryExpressionContext ctx) {
final List<ParseTree> children = ctx.children;
final SqmQueryPart queryPart = (SqmQueryPart) children.get( 1 ).accept( this );
if ( children.size() > 3 ) {
visitQueryOrder( queryPart, (HqlParser.QueryOrderContext) children.get( 3 ) );
}
@Override
public SqmQueryPart visitNestedQueryGroup(HqlParser.NestedQueryGroupContext ctx) {
return (SqmQueryPart) ctx.queryGroup().accept( this );
return queryPart;
}
@Override
@ -515,20 +523,12 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
setCurrentQueryPart( queryGroup );
final List<SqmSelection> firstSelections = firstQueryPart.getFirstQuerySpec().getSelectClause().getSelections();
final int firstSelectionSize = firstSelections.size();
final ParseTree maybeOrderContext = children.get( 1 );
int i;
if (maybeOrderContext instanceof HqlParser.QueryOrderContext ) {
visitQueryOrder( firstQueryPart, (HqlParser.QueryOrderContext) maybeOrderContext );
i = 2;
}
else {
i = 1;
}
final int size = children.size();
final SqmCreationProcessingState firstProcessingState = processingStateStack.pop();
for ( ; i < size; i += 2 ) {
for ( int i = 1; i < size; i += 2 ) {
final SetOperator operator = visitSetOperator( (HqlParser.SetOperatorContext) children.get( i ) );
final ParseTree parseTree = children.get( i + 1 );
final HqlParser.SimpleQueryExpressionContext simpleQueryCtx =
(HqlParser.SimpleQueryExpressionContext) children.get( i + 1 );
final List<SqmQueryPart<?>> queryParts;
if ( queryGroup.getSetOperator() == null || queryGroup.getSetOperator() == operator ) {
queryGroup.setSetOperator( operator );
@ -554,15 +554,28 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
this
)
);
if ( parseTree instanceof HqlParser.QuerySpecContext ) {
final List<ParseTree> subChildren = simpleQueryCtx.children;
if ( subChildren.get( 0 ) instanceof HqlParser.QuerySpecContext ) {
final SqmQuerySpec<?> querySpec = new SqmQuerySpec<>( creationContext.getNodeBuilder() );
queryParts.add( querySpec );
queryPart = visitQuerySpec( (HqlParser.QuerySpecContext) parseTree );
queryPart = visitQuerySpecExpression( (HqlParser.QuerySpecExpressionContext) simpleQueryCtx );
}
else {
queryPart = (SqmQueryPart<?>) children.get( i + 2 ).accept( this );
try {
final SqmSelectStatement selectStatement = new SqmSelectStatement( creationContext.getNodeBuilder() );
processingStateStack.push(
new SqmQuerySpecCreationProcessingStateStandardImpl(
processingStateStack.getCurrent(),
selectStatement,
this
)
);
queryPart = visitNestedQueryExpression( (HqlParser.NestedQueryExpressionContext) simpleQueryCtx );
queryParts.add( queryPart );
i += 2;
}
finally {
processingStateStack.pop();
}
}
}
finally {
@ -3867,7 +3880,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
);
try {
visitQueryExpression( queryExpressionContext );
queryExpressionContext.accept( this );
return subQuery;
}
finally {

View File

@ -293,7 +293,7 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
interpretation.getSqlAst()
);
final Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamsXref = SqmUtil.generateJdbcParamsXref(
final Map<QueryParameterImplementor<?>, Map<SqmParameter, List<List<JdbcParameter>>>> jdbcParamsXref = SqmUtil.generateJdbcParamsXref(
domainParameterXref,
interpretation::getJdbcParamsBySqmParam
);
@ -326,13 +326,13 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
private static class CacheableSqmInterpretation {
private final JdbcSelect jdbcSelect;
private final FromClauseAccess tableGroupAccess;
private final Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamsXref;
private final Map<QueryParameterImplementor<?>, Map<SqmParameter, List<List<JdbcParameter>>>> jdbcParamsXref;
private transient JdbcParameterBindings firstParameterBindings;
CacheableSqmInterpretation(
JdbcSelect jdbcSelect,
FromClauseAccess tableGroupAccess,
Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamsXref,
Map<QueryParameterImplementor<?>, Map<SqmParameter, List<List<JdbcParameter>>>> jdbcParamsXref,
JdbcParameterBindings firstParameterBindings) {
this.jdbcSelect = jdbcSelect;
this.tableGroupAccess = tableGroupAccess;
@ -348,7 +348,7 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
return tableGroupAccess;
}
Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> getJdbcParamsXref() {
Map<QueryParameterImplementor<?>, Map<SqmParameter, List<List<JdbcParameter>>>> getJdbcParamsXref() {
return jdbcParamsXref;
}

View File

@ -50,7 +50,7 @@ public class SimpleDeleteQueryPlan implements NonSelectQueryPlan {
private JdbcDelete jdbcDelete;
private SqmTranslation<DeleteStatement> sqmInterpretation;
private Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamsXref;
private Map<QueryParameterImplementor<?>, Map<SqmParameter, List<List<JdbcParameter>>>> jdbcParamsXref;
public SimpleDeleteQueryPlan(
EntityMappingType entityDescriptor,

View File

@ -40,7 +40,7 @@ public class SimpleInsertQueryPlan implements NonSelectQueryPlan {
private JdbcInsert jdbcInsert;
private FromClauseAccess tableGroupAccess;
private Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamsXref;
private Map<QueryParameterImplementor<?>, Map<SqmParameter, List<List<JdbcParameter>>>> jdbcParamsXref;
public SimpleInsertQueryPlan(
SqmInsertStatement sqmInsert,

View File

@ -40,7 +40,7 @@ public class SimpleUpdateQueryPlan implements NonSelectQueryPlan {
private JdbcUpdate jdbcUpdate;
private FromClauseAccess tableGroupAccess;
private Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamsXref;
private Map<QueryParameterImplementor<?>, Map<SqmParameter, List<List<JdbcParameter>>>> jdbcParamsXref;
public SimpleUpdateQueryPlan(
SqmUpdateStatement sqmUpdate,

View File

@ -79,7 +79,7 @@ public class SqmUtil {
}
}
public static Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> generateJdbcParamsXref(
public static Map<QueryParameterImplementor<?>, Map<SqmParameter, List<List<JdbcParameter>>>> generateJdbcParamsXref(
DomainParameterXref domainParameterXref,
JdbcParameterBySqmParameterAccess jdbcParameterBySqmParameterAccess) {
if ( domainParameterXref == null || !domainParameterXref.hasParameters() ) {
@ -87,13 +87,13 @@ public class SqmUtil {
}
final int queryParameterCount = domainParameterXref.getQueryParameterCount();
final Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> result = new IdentityHashMap<>( queryParameterCount );
final Map<QueryParameterImplementor<?>, Map<SqmParameter, List<List<JdbcParameter>>>> result = new IdentityHashMap<>( queryParameterCount );
for ( Map.Entry<QueryParameterImplementor<?>, List<SqmParameter>> entry : domainParameterXref.getSqmParamByQueryParam().entrySet() ) {
final QueryParameterImplementor<?> queryParam = entry.getKey();
final List<SqmParameter> sqmParams = entry.getValue();
final Map<SqmParameter, List<JdbcParameter>> sqmParamMap = result.computeIfAbsent(
final Map<SqmParameter, List<List<JdbcParameter>>> sqmParamMap = result.computeIfAbsent(
queryParam,
qp -> new IdentityHashMap<>( sqmParams.size() )
);
@ -157,7 +157,7 @@ public class SqmUtil {
public static JdbcParameterBindings createJdbcParameterBindings(
QueryParameterBindings domainParamBindings,
DomainParameterXref domainParameterXref,
Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamXref,
Map<QueryParameterImplementor<?>, Map<SqmParameter, List<List<JdbcParameter>>>> jdbcParamXref,
MappingMetamodel domainModel,
Function<NavigablePath, TableGroup> tableGroupLocator,
SharedSessionContractImplementor session) {
@ -177,16 +177,17 @@ public class SqmUtil {
session.getFactory()
);
final Map<SqmParameter, List<JdbcParameter>> jdbcParamMap = jdbcParamXref.get( queryParam );
final Map<SqmParameter, List<List<JdbcParameter>>> jdbcParamMap = jdbcParamXref.get( queryParam );
for ( SqmParameter sqmParameter : sqmParameters ) {
final List<JdbcParameter> jdbcParams = jdbcParamMap.get( sqmParameter );
if ( ! domainParamBinding.isBound() ) {
final List<List<JdbcParameter>> jdbcParamsBinds = jdbcParamMap.get( sqmParameter );
if ( !domainParamBinding.isBound() ) {
final MappingModelExpressable mappingExpressable = SqmMappingModelHelper.resolveMappingModelExpressable(
sqmParameter,
domainModel,
tableGroupLocator
);
for ( int i = 0; i < jdbcParamsBinds.size(); i++ ) {
final List<JdbcParameter> jdbcParams = jdbcParamsBinds.get( i );
mappingExpressable.forEachJdbcType(
(position, jdbcType) -> {
jdbcParameterBindings.addBinding(
@ -196,12 +197,23 @@ public class SqmUtil {
}
);
}
}
else if ( domainParamBinding.isMultiValued() ) {
final Collection<?> bindValues = domainParamBinding.getBindValues();
final Iterator<?> valueItr = bindValues.iterator();
// the original SqmParameter is the one we are processing.. create a binding for it..
createValueBindings( jdbcParameterBindings, domainParamBinding, parameterType, jdbcParams, valueItr.next(), session );
for ( int i = 0; i < jdbcParamsBinds.size(); i++ ) {
final List<JdbcParameter> jdbcParams = jdbcParamsBinds.get( i );
createValueBindings(
jdbcParameterBindings,
domainParamBinding,
parameterType,
jdbcParams,
valueItr.next(),
session
);
}
// an then one for each of the expansions
final List<SqmParameter> expansions = domainParameterXref.getExpansions( sqmParameter );
@ -209,23 +221,45 @@ public class SqmUtil {
int expansionPosition = 0;
while ( valueItr.hasNext() ) {
final SqmParameter expansionSqmParam = expansions.get( expansionPosition++ );
final List<JdbcParameter> expansionJdbcParams = jdbcParamMap.get( expansionSqmParam );
createValueBindings( jdbcParameterBindings, domainParamBinding, parameterType, expansionJdbcParams, valueItr.next(), session );
final List<List<JdbcParameter>> jdbcParamBinds = jdbcParamMap.get( expansionSqmParam );
for ( int i = 0; i < jdbcParamBinds.size(); i++ ) {
List<JdbcParameter> expansionJdbcParams = jdbcParamBinds.get( i );
createValueBindings(
jdbcParameterBindings,
domainParamBinding,
parameterType,
expansionJdbcParams,
valueItr.next(),
session
);
}
}
}
else if ( domainParamBinding.getBindValue() == null ) {
assert jdbcParams != null;
for ( int i = 0; i < jdbcParams.size(); i++ ) {
final JdbcParameter jdbcParameter = jdbcParams.get( i );
for ( int i = 0; i < jdbcParamsBinds.size(); i++ ) {
final List<JdbcParameter> jdbcParams = jdbcParamsBinds.get( i );
for ( int j = 0; j < jdbcParams.size(); j++ ) {
final JdbcParameter jdbcParameter = jdbcParams.get( j );
jdbcParameterBindings.addBinding(
jdbcParameter,
new JdbcParameterBindingImpl( null, null )
);
}
}
}
else {
final Object bindValue = domainParamBinding.getBindValue();
createValueBindings( jdbcParameterBindings, domainParamBinding, parameterType, jdbcParams, bindValue, session );
for ( int i = 0; i < jdbcParamsBinds.size(); i++ ) {
final List<JdbcParameter> jdbcParams = jdbcParamsBinds.get( i );
createValueBindings(
jdbcParameterBindings,
domainParamBinding,
parameterType,
jdbcParams,
bindValue,
session
);
}
}
}
}

View File

@ -198,8 +198,8 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
final Expression expression = super.consumeSqmParameter( sqmParameter );
final List<JdbcParameter> jdbcParameters = getJdbcParamsBySqmParam().get( sqmParameter );
parameterResolutionConsumer.accept( sqmParameter, jdbcParameters );
final List<List<JdbcParameter>> jdbcParameters = getJdbcParamsBySqmParam().get( sqmParameter );
parameterResolutionConsumer.accept( sqmParameter, jdbcParameters.get( jdbcParameters.size() - 1 ) );
return expression;
}

View File

@ -90,13 +90,6 @@ public class CteStrategy implements SqmMultiTableMutationStrategy {
);
}
if ( !dialect.supportsRowValueConstructorSyntaxInInList() ) {
throw new UnsupportedOperationException(
getClass().getSimpleName() +
" can only be used with Dialects that support IN clause row-value expressions (for composite identifiers)"
);
}
this.cteTable = new SqmCteTable( TABLE_NAME, rootDescriptor );
}

View File

@ -99,7 +99,7 @@ public class IdTableHelper {
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
try {
final String dropCommand = exporter.getSqlCreateCommand( idTable );
final String dropCommand = exporter.getSqlDropCommand( idTable );
logStatement( dropCommand, jdbcServices );
try (Statement statement = connection.createStatement()) {

View File

@ -125,7 +125,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
final TableReference hierarchyRootTableReference = deletingTableGroup.resolveTableReference( hierarchyRootTableName );
assert hierarchyRootTableReference != null;
final Map<SqmParameter, List<JdbcParameter>> parameterResolutions;
final Map<SqmParameter, List<List<JdbcParameter>>> parameterResolutions;
if ( domainParameterXref.getSqmParameterCount() == 0 ) {
parameterResolutions = Collections.emptyMap();
}
@ -146,7 +146,10 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
needsIdTableWrapper.set( true );
}
},
parameterResolutions::put
(sqmParameter, jdbcParameters) -> parameterResolutions.computeIfAbsent(
sqmParameter,
k -> new ArrayList<>( 1 )
).add( jdbcParameters )
);
final FilterPredicate filterPredicate = FilterHelper.createFilterPredicate(
@ -183,7 +186,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
private int executeWithoutIdTable(
Predicate suppliedPredicate,
TableGroup tableGroup,
Map<SqmParameter, List<JdbcParameter>> restrictionSqmParameterResolutions,
Map<SqmParameter, List<List<JdbcParameter>>> restrictionSqmParameterResolutions,
SqlExpressionResolver sqlExpressionResolver,
ExecutionContext executionContext) {
final EntityPersister rootEntityPersister;
@ -354,7 +357,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
private int executeWithIdTable(
Predicate predicate,
TableGroup deletingTableGroup,
Map<SqmParameter, List<JdbcParameter>> restrictionSqmParameterResolutions,
Map<SqmParameter, List<List<JdbcParameter>>> restrictionSqmParameterResolutions,
ExecutionContext executionContext) {
final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(
executionContext.getQueryParameterBindings(),

View File

@ -134,7 +134,7 @@ public class TableBasedUpdateHandler
final TableReference hierarchyRootTableReference = updatingTableGroup.resolveTableReference( hierarchyRootTableName );
assert hierarchyRootTableReference != null;
final Map<SqmParameter,List<JdbcParameter>> parameterResolutions;
final Map<SqmParameter, List<List<JdbcParameter>>> parameterResolutions;
if ( domainParameterXref.getSqmParameterCount() == 0 ) {
parameterResolutions = Collections.emptyMap();
}
@ -151,7 +151,10 @@ public class TableBasedUpdateHandler
converterDelegate.visitSetClause(
getSqmDeleteOrUpdateStatement().getSetClause(),
assignments::add,
parameterResolutions::put
(sqmParameter, jdbcParameters) -> parameterResolutions.computeIfAbsent(
sqmParameter,
k -> new ArrayList<>( 1 )
).add( jdbcParameters )
);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -167,7 +170,10 @@ public class TableBasedUpdateHandler
predicate = converterDelegate.visitWhereClause(
whereClause,
columnReference -> {},
parameterResolutions::put
(sqmParameter, jdbcParameters) -> parameterResolutions.computeIfAbsent(
sqmParameter,
k -> new ArrayList<>( 1 )
).add( jdbcParameters )
);
assert predicate != null;
}

View File

@ -78,7 +78,7 @@ public class UpdateExecutionDelegate implements TableBasedUpdateHandler.Executio
Map<String, TableReference> tableReferenceByAlias,
List<Assignment> assignments,
Predicate suppliedPredicate,
Map<SqmParameter, List<JdbcParameter>> parameterResolutions,
Map<SqmParameter, List<List<JdbcParameter>>> parameterResolutions,
ExecutionContext executionContext) {
this.sqmUpdate = sqmUpdate;
this.sqmConverter = sqmConverter;

View File

@ -21,5 +21,5 @@ public interface JdbcParameterBySqmParameterAccess {
/**
* The mapping between an SqmParameter and all of its JDBC parameters
*/
Map<SqmParameter, List<JdbcParameter>> getJdbcParamsBySqmParam();
Map<SqmParameter, List<List<JdbcParameter>>> getJdbcParamsBySqmParam();
}

View File

@ -26,6 +26,7 @@ import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.boot.model.process.internal.InferredBasicValueResolver;
import org.hibernate.dialect.function.TimestampaddFunction;
import org.hibernate.dialect.function.TimestampdiffFunction;
import org.hibernate.engine.FetchTiming;
@ -41,9 +42,11 @@ import org.hibernate.loader.MultipleBagFetchException;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.MappingMetamodel;
import org.hibernate.metamodel.mapping.Association;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
@ -265,8 +268,10 @@ import org.hibernate.sql.results.graph.entity.EntityResultGraphNode;
import org.hibernate.sql.results.graph.instantiation.internal.DynamicInstantiation;
import org.hibernate.sql.results.internal.SqlSelectionImpl;
import org.hibernate.sql.results.internal.StandardEntityGraphTraversalStateImpl;
import org.hibernate.type.BasicType;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
import org.hibernate.type.spi.TypeConfiguration;
import org.jboss.logging.Logger;
@ -284,7 +289,7 @@ import static org.hibernate.type.spi.TypeConfiguration.isDuration;
* @author Steve Ebersole
*/
public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends BaseSemanticQueryWalker
implements SqmTranslator<T>, DomainResultCreationState {
implements SqmTranslator<T>, DomainResultCreationState, SqlTypeDescriptorIndicators {
private static final Logger log = Logger.getLogger( BaseSqmToSqlAstConverter.class );
@ -403,6 +408,19 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return statement;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// SqlTypeDescriptorIndicators
@Override
public TypeConfiguration getTypeConfiguration() {
return creationContext.getSessionFactory().getTypeConfiguration();
}
@Override
public int getPreferredSqlTypeCodeForBoolean() {
return creationContext.getSessionFactory().getSessionFactoryOptions().getPreferredSqlTypeCodeForBoolean();
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// FromClauseAccess
@ -578,7 +596,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
@Override
public List<Assignment> visitSetClause(SqmSetClause setClause) {
final List<Assignment> assignments = new ArrayList<>();
final List<Assignment> assignments = new ArrayList<>( setClause.getAssignments().size() );
for ( SqmAssignment sqmAssignment : setClause.getAssignments() ) {
final List<ColumnReference> targetColumnReferences = new ArrayList<>();
@ -663,14 +681,15 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
);
getJdbcParamsBySqmParam().put( sqmParameter, jdbcParametersForSqm );
getJdbcParamsBySqmParam().computeIfAbsent( sqmParameter, k -> new ArrayList<>( 1 ) )
.add( jdbcParametersForSqm );
}
else {
final MappingMetamodel domainModel = getCreationContext().getDomainModel();
final Expression valueExpression = (Expression) sqmAssignment.getValue().accept( this );
final int valueExprJdbcCount = valueExpression.getExpressionType().getJdbcTypeCount();
final int assignedPathJdbcCount = assignedPathInterpretation.getExpressionType().getJdbcTypeCount();
final int valueExprJdbcCount = getKeyExpressable( valueExpression.getExpressionType() ).getJdbcTypeCount();
final int assignedPathJdbcCount = getKeyExpressable( assignedPathInterpretation.getExpressionType() )
.getJdbcTypeCount();
if ( valueExprJdbcCount != assignedPathJdbcCount ) {
SqlTreeCreationLogger.LOGGER.debugf(
@ -2021,24 +2040,50 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
@Override
public Expression visitLiteral(SqmLiteral<?> literal) {
if ( literal instanceof SqmLiteralNull ) {
return new NullnessLiteral( inferableTypeAccessStack.getCurrent().get() );
final MappingModelExpressable mappingModelExpressable = inferableTypeAccessStack.getCurrent().get();
if ( mappingModelExpressable instanceof BasicValuedMapping ) {
return new NullnessLiteral( mappingModelExpressable );
}
return new QueryLiteral<>(
literal.getLiteralValue(),
(BasicValuedMapping) SqmMappingModelHelper.resolveMappingModelExpressable(
final MappingModelExpressable keyExpressable = getKeyExpressable( mappingModelExpressable );
final List<Expression> expressions = new ArrayList<>( keyExpressable.getJdbcTypeCount() );
keyExpressable.forEachJdbcType(
(index, jdbcMapping) -> expressions.add(
new QueryLiteral<>(
null,
(BasicValuedMapping) jdbcMapping
)
)
);
return new SqlTuple( expressions, mappingModelExpressable );
}
MappingModelExpressable expressable = SqmMappingModelHelper.resolveMappingModelExpressable(
literal,
getCreationContext().getDomainModel(),
getFromClauseAccess()::findTableGroup
)
);
if ( expressable instanceof BasicType<?> ) {
expressable = InferredBasicValueResolver.resolveSqlTypeIndicators( this, (BasicType<?>) expressable );
}
return new QueryLiteral<>(
literal.getLiteralValue(),
(BasicValuedMapping) expressable
);
}
private final Map<SqmParameter, List<JdbcParameter>> jdbcParamsBySqmParam = new IdentityHashMap<>();
private MappingModelExpressable<?> getKeyExpressable(MappingModelExpressable<?> mappingModelExpressable) {
if ( mappingModelExpressable instanceof EntityAssociationMapping ) {
return ( (EntityAssociationMapping) mappingModelExpressable ).getKeyTargetMatchPart();
}
else {
return mappingModelExpressable;
}
}
private final Map<SqmParameter, List<List<JdbcParameter>>> jdbcParamsBySqmParam = new IdentityHashMap<>();
private final JdbcParameters jdbcParameters = new JdbcParametersImpl();
@Override
public Map<SqmParameter, List<JdbcParameter>> getJdbcParamsBySqmParam() {
public Map<SqmParameter, List<List<JdbcParameter>>> getJdbcParamsBySqmParam() {
return jdbcParamsBySqmParam;
}
@ -2059,7 +2104,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
);
this.jdbcParameters.addParameters( jdbcParametersForSqm );
this.jdbcParamsBySqmParam.put( sqmParameter, jdbcParametersForSqm );
this.jdbcParamsBySqmParam.computeIfAbsent( sqmParameter, k -> new ArrayList<>( 1 ) )
.add( jdbcParametersForSqm );
final QueryParameterImplementor<?> queryParameter = domainParameterXref.getQueryParameter( sqmParameter );
final QueryParameterBinding<?> binding = domainParameterBindings.getBinding( queryParameter );
@ -2268,8 +2314,12 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
public Object visitCastTarget(SqmCastTarget target) {
shallownessStack.push( Shallowness.FUNCTION );
try {
BasicValuedMapping targetType = (BasicValuedMapping) target.getType();
if ( targetType instanceof BasicType<?> ) {
targetType = InferredBasicValueResolver.resolveSqlTypeIndicators( this, (BasicType<?>) targetType );
}
return new CastTarget(
(BasicValuedMapping) target.getType(),
targetType,
target.getLength(),
target.getPrecision(),
target.getScale()
@ -2818,9 +2868,9 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
if ( type instanceof SqmExpressable ) {
adjustedTimestampType = (SqmExpressable) type;
}
// else if (type instanceof BasicValuedMapping) {
// adjustedTimestampType = ((BasicValuedMapping) type).getBasicType();
// }
else if (type instanceof AttributeMapping ) {
adjustedTimestampType = (SqmExpressable) ( (AttributeMapping) type ).getMappedType();
}
else {
// else we know it has not been transformed
adjustedTimestampType = expression.getLeftHandOperand().getNodeType();

View File

@ -22,7 +22,7 @@ import org.hibernate.sql.ast.tree.expression.JdbcParameter;
*/
public interface SqmTranslation<T extends Statement> {
T getSqlAst();
Map<SqmParameter, List<JdbcParameter>> getJdbcParamsBySqmParam();
Map<SqmParameter, List<List<JdbcParameter>>> getJdbcParamsBySqmParam();
SqlExpressionResolver getSqlExpressionResolver();
FromClauseAccess getFromClauseAccess();
}

View File

@ -21,13 +21,13 @@ import org.hibernate.sql.ast.tree.expression.JdbcParameter;
public class StandardSqmTranslation<T extends Statement> implements SqmTranslation<T> {
private final T sqlAst;
private final Map<SqmParameter, List<JdbcParameter>> jdbcParamMap;
private final Map<SqmParameter, List<List<JdbcParameter>>> jdbcParamMap;
private final SqlExpressionResolver sqlExpressionResolver;
private final FromClauseAccess fromClauseAccess;
public StandardSqmTranslation(
T sqlAst,
Map<SqmParameter, List<JdbcParameter>> jdbcParamMap,
Map<SqmParameter, List<List<JdbcParameter>>> jdbcParamMap,
SqlExpressionResolver sqlExpressionResolver,
FromClauseAccess fromClauseAccess) {
this.sqlAst = sqlAst;
@ -42,7 +42,7 @@ public class StandardSqmTranslation<T extends Statement> implements SqmTranslati
}
@Override
public Map<SqmParameter, List<JdbcParameter>> getJdbcParamsBySqmParam() {
public Map<SqmParameter, List<List<JdbcParameter>>> getJdbcParamsBySqmParam() {
return jdbcParamMap;
}

View File

@ -62,6 +62,9 @@ public final class Template {
KEYWORDS.add("all");
KEYWORDS.add("union");
KEYWORDS.add("minus");
KEYWORDS.add("except");
KEYWORDS.add("intersect");
KEYWORDS.add("partition");
BEFORE_TABLE_KEYWORDS.add("from");
BEFORE_TABLE_KEYWORDS.add("join");

View File

@ -11,12 +11,11 @@ import java.util.Set;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
/**
* @author Steve Ebersole
*/
public interface SqlAstTranslator<T extends JdbcOperation> extends SqlAstWalker, SqlTypeDescriptorIndicators {
public interface SqlAstTranslator<T extends JdbcOperation> extends SqlAstWalker {
/**
* Not the best spot for this. Its the table names collected while walking the SQL AST.
* Its ok here because the translator is consider a one-time-use. It just needs to be called

View File

@ -121,16 +121,13 @@ import org.hibernate.type.IntegerType;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.sql.JdbcLiteralFormatter;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.query.TemporalUnit.NANOSECOND;
/**
* @author Steve Ebersole
*/
public abstract class AbstractSqlAstWalker
implements SqlAstWalker, SqlTypeDescriptorIndicators, SqlAppender {
public abstract class AbstractSqlAstWalker implements SqlAstWalker, SqlAppender {
private static final QueryLiteral<Integer> ONE_LITERAL = new QueryLiteral<>( 1, IntegerType.INSTANCE );
@ -150,6 +147,7 @@ public abstract class AbstractSqlAstWalker
private final Dialect dialect;
private String dmlTargetTableAlias;
private boolean needsSelectAliases;
private QueryPart queryPartForRowNumbering;
private int queryPartForRowNumberingAliasCounter;
private int queryGroupAliasCounter;
@ -468,12 +466,21 @@ public abstract class AbstractSqlAstWalker
statement.getSourceSelectStatement().accept( this );
}
else {
visitValuesList( statement.getValuesList() );
}
visitReturningColumns( statement );
}
private void renderImplicitTargetColumnSpec() {
}
protected void visitValuesList(List<Values> valuesList) {
appendSql("values");
boolean firstTuple = true;
final Stack<Clause> clauseStack = getClauseStack();
try {
clauseStack.push( Clause.VALUES );
for ( Values values : statement.getValuesList() ) {
for ( Values values : valuesList ) {
if ( firstTuple ) {
firstTuple = false;
}
@ -498,11 +505,6 @@ public abstract class AbstractSqlAstWalker
clauseStack.pop();
}
}
visitReturningColumns( statement );
}
private void renderImplicitTargetColumnSpec() {
}
protected void visitReturningColumns(MutationStatement mutationStatement) {
final List<ColumnReference> returningColumns = mutationStatement.getReturningColumns();
@ -623,13 +625,17 @@ public abstract class AbstractSqlAstWalker
@Override
public void visitQueryGroup(QueryGroup queryGroup) {
final QueryPart queryPartForRowNumbering = this.queryPartForRowNumbering;
final boolean needsSelectAliases = this.needsSelectAliases;
try {
String queryGroupAlias = null;
if ( queryPartForRowNumbering != queryPartStack.getCurrent() ) {
final QueryPart currentQueryPart = queryPartStack.getCurrent();
if ( currentQueryPart != null && queryPartForRowNumbering != currentQueryPart ) {
this.queryPartForRowNumbering = null;
this.needsSelectAliases = false;
}
// If we do row counting for this query group, the wrapper select is added by the caller
if ( queryPartForRowNumbering != queryGroup && !queryGroup.isRoot() ) {
this.needsSelectAliases = true;
queryGroupAlias = "grp_" + queryGroupAliasCounter + "_";
queryGroupAliasCounter++;
appendSql( "select " );
@ -656,17 +662,39 @@ public abstract class AbstractSqlAstWalker
finally {
queryPartStack.pop();
this.queryPartForRowNumbering = queryPartForRowNumbering;
this.needsSelectAliases = needsSelectAliases;
}
}
@Override
public void visitQuerySpec(QuerySpec querySpec) {
final QueryPart queryPartForRowNumbering = this.queryPartForRowNumbering;
final boolean needsSelectAliases = this.needsSelectAliases;
try {
if ( queryPartForRowNumbering != queryPartStack.getCurrent() ) {
final QueryPart currentQueryPart = queryPartStack.getCurrent();
if ( currentQueryPart != null && queryPartForRowNumbering != currentQueryPart ) {
this.queryPartForRowNumbering = null;
}
final boolean needsParenthesis = !querySpec.isRoot() && !( queryPartStack.getCurrent() instanceof QueryGroup );
String queryGroupAlias = "";
final boolean needsParenthesis;
if ( currentQueryPart instanceof QueryGroup ) {
// We always need query wrapping if we are in a query group and the query part has a fetch clause
if ( needsParenthesis = querySpec.hasOffsetOrFetchClause() ) {
// If the parent is a query group with a fetch clause, we must use an alias
// Some DBMS don't support grouping query expressions and need a select wrapper
if ( !supportsSimpleQueryGrouping() || currentQueryPart.hasOffsetOrFetchClause() ) {
this.needsSelectAliases = true;
queryGroupAlias = " grp_" + queryGroupAliasCounter + "_";
queryGroupAliasCounter++;
appendSql( "select" );
appendSql( queryGroupAlias );
appendSql( ".* from " );
}
}
}
else {
needsParenthesis = !querySpec.isRoot();
}
queryPartStack.push( querySpec );
if ( needsParenthesis ) {
appendSql( "(" );
@ -681,14 +709,20 @@ public abstract class AbstractSqlAstWalker
if ( needsParenthesis ) {
appendSql( ")" );
appendSql( queryGroupAlias );
}
}
finally {
queryPartStack.pop();
this.queryPartForRowNumbering = queryPartForRowNumbering;
this.needsSelectAliases = needsSelectAliases;
}
}
protected boolean supportsSimpleQueryGrouping() {
return true;
}
protected final void visitWhereClause(QuerySpec querySpec) {
final Predicate whereClauseRestrictions = querySpec.getWhereClauseRestrictions();
if ( whereClauseRestrictions != null && !whereClauseRestrictions.isEmpty() ) {
@ -802,8 +836,12 @@ public abstract class AbstractSqlAstWalker
appendSql( "()" );
break;
case SUBQUERY:
appendSql( "(select 1 " );
appendSql( dialect.getFromDual() );
appendSql( "(select 1" );
final String fromDual = dialect.getFromDual();
if ( !fromDual.isEmpty() ) {
appendSql( " " );
appendSql( fromDual );
}
appendSql( ')' );
break;
case COLUMN_REFERENCE:
@ -965,14 +1003,14 @@ public abstract class AbstractSqlAstWalker
}
}
private void renderExpressionsAsSubquery(final List<? extends Expression> expressions) {
protected void renderExpressionsAsSubquery(final List<? extends Expression> expressions) {
clauseStack.push( Clause.SELECT );
try {
appendSql( "select " );
renderCommaSeparated( expressions );
String fromDual = dialect.getFromDual();
final String fromDual = dialect.getFromDual();
if ( !fromDual.isEmpty() ) {
appendSql( " " );
appendSql( fromDual );
@ -1634,8 +1672,10 @@ public abstract class AbstractSqlAstWalker
FetchClauseType fetchClauseType,
boolean emulateFetchClause) {
final QueryPart queryPartForRowNumbering = this.queryPartForRowNumbering;
final boolean needsSelectAliases = this.needsSelectAliases;
try {
this.queryPartForRowNumbering = queryPart;
this.needsSelectAliases = true;
final String alias = "r_" + queryPartForRowNumberingAliasCounter + "_";
queryPartForRowNumberingAliasCounter++;
appendSql( "select " );
@ -1736,6 +1776,7 @@ public abstract class AbstractSqlAstWalker
}
finally {
this.queryPartForRowNumbering = queryPartForRowNumbering;
this.needsSelectAliases = needsSelectAliases;
}
}
@ -1761,25 +1802,21 @@ public abstract class AbstractSqlAstWalker
protected void visitSqlSelections(SelectClause selectClause) {
final List<SqlSelection> sqlSelections = selectClause.getSqlSelections();
final int size = sqlSelections.size();
if ( queryPartForRowNumbering == null ) {
if ( needsSelectAliases ) {
String separator = NO_SEPARATOR;
for ( int i = 0; i < size; i++ ) {
final SqlSelection sqlSelection = sqlSelections.get( i );
appendSql( separator );
sqlSelection.accept( this );
separator = COMA_SEPARATOR;
}
}
else {
for ( int i = 0; i < size; i++ ) {
final SqlSelection sqlSelection = sqlSelections.get( i );
sqlSelection.accept( this );
appendSql( " c" );
appendSql( Integer.toString( i ) );
appendSql( COMA_SEPARATOR );
separator = COMA_SEPARATOR;
}
switch ( getFetchClauseTypeForRowNumbering( queryPartForRowNumbering ) ) {
if ( queryPartForRowNumbering != null ) {
final FetchClauseType fetchClauseType = getFetchClauseTypeForRowNumbering( queryPartForRowNumbering );
if ( fetchClauseType != null ) {
appendSql( separator );
switch ( fetchClauseType ) {
case PERCENT_ONLY:
appendSql( "count(*) over () cnt," );
case ROWS_ONLY:
@ -1808,6 +1845,17 @@ public abstract class AbstractSqlAstWalker
}
}
}
}
else {
String separator = NO_SEPARATOR;
for ( int i = 0; i < size; i++ ) {
final SqlSelection sqlSelection = sqlSelections.get( i );
appendSql( separator );
sqlSelection.accept( this );
separator = COMA_SEPARATOR;
}
}
}
protected FetchClauseType getFetchClauseTypeForRowNumbering(QueryPart queryPartForRowNumbering) {
return queryPartForRowNumbering.getFetchClauseType();
@ -2098,7 +2146,7 @@ public abstract class AbstractSqlAstWalker
@Override
public void visitExtractUnit(ExtractUnit extractUnit) {
appendSql( sessionFactory.getJdbcServices().getDialect().translateExtractField( extractUnit.getUnit() ) );
appendSql( getDialect().translateExtractField( extractUnit.getUnit() ) );
}
@Override
@ -2108,7 +2156,7 @@ public abstract class AbstractSqlAstWalker
@Override
public void visitFormat(Format format) {
final String dialectFormat = sessionFactory.getJdbcServices().getDialect().translateDatetimeFormat( format.getFormat() );
final String dialectFormat = getDialect().translateDatetimeFormat( format.getFormat() );
appendSql( "'" );
appendSql( dialectFormat );
appendSql( "'" );
@ -2846,8 +2894,10 @@ public abstract class AbstractSqlAstWalker
}
final QueryPart queryPartForRowNumbering = this.queryPartForRowNumbering;
final boolean needsSelectAliases = this.needsSelectAliases;
try {
this.queryPartForRowNumbering = null;
this.needsSelectAliases = false;
queryPartStack.push( subQuery );
appendSql( "exists (select 1" );
visitFromClause( subQuery.getFromClause() );
@ -2904,6 +2954,7 @@ public abstract class AbstractSqlAstWalker
finally {
queryPartStack.pop();
this.queryPartForRowNumbering = queryPartForRowNumbering;
this.needsSelectAliases = needsSelectAliases;
}
}
else {
@ -2931,8 +2982,10 @@ public abstract class AbstractSqlAstWalker
appendSql( " " );
final QueryPart queryPartForRowNumbering = this.queryPartForRowNumbering;
final boolean needsSelectAliases = this.needsSelectAliases;
try {
this.queryPartForRowNumbering = null;
this.needsSelectAliases = false;
queryPartStack.push( subQuery );
appendSql( "(" );
visitSelectClause( subQuery.getSelectClause() );
@ -2964,6 +3017,7 @@ public abstract class AbstractSqlAstWalker
finally {
queryPartStack.pop();
this.queryPartForRowNumbering = queryPartForRowNumbering;
this.needsSelectAliases = needsSelectAliases;
}
}
else {
@ -3198,22 +3252,4 @@ public abstract class AbstractSqlAstWalker
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// JdbcRecommendedSqlTypeMappingContext
@Override
public boolean isNationalized() {
return false;
}
@Override
public boolean isLob() {
return false;
}
@Override
public TypeConfiguration getTypeConfiguration() {
return getSessionFactory().getTypeConfiguration();
}
}

View File

@ -1,16 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.sql.ast.spi;
import org.hibernate.sql.ast.SqlAstWalker;
/**
* @author Steve Ebersole
* @author Andrea Boriero
*/
public interface SqlSelectAstWalker extends SqlAstWalker {
}

View File

@ -100,7 +100,7 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor {
rowTransformer,
(sql) -> executionContext.getSession().getJdbcCoordinator().getStatementPreparer().prepareQueryStatement(
sql,
true,
false,
scrollMode
),
ScrollableResultsConsumer.instance()

View File

@ -155,7 +155,7 @@ public class DeferredResultSetAccess extends AbstractResultSetAccess {
// For dialects that don't support an offset clause
final int rowsToSkip;
if ( !jdbcSelect.usesLimitParameters() && limit != null && limit.getFirstRow() != null && !limitHandler.supportsOffset() ) {
if ( !jdbcSelect.usesLimitParameters() && limit != null && limit.getFirstRow() != null && !limitHandler.supportsLimitOffset() ) {
rowsToSkip = limit.getFirstRow();
}
else {

View File

@ -9,6 +9,7 @@ package org.hibernate.type;
import java.io.Serializable;
import org.hibernate.dialect.Dialect;
import org.hibernate.query.CastType;
import org.hibernate.type.descriptor.java.BooleanTypeDescriptor;
import org.hibernate.type.descriptor.sql.IntegerTypeDescriptor;
@ -46,4 +47,8 @@ public class NumericBooleanType
public String objectToSQLString(Boolean value, Dialect dialect) {
return value ? "1" : "0";
}
@Override
public CastType getCastType() {
return CastType.INTEGER_BOOLEAN;
}
}

View File

@ -14,6 +14,7 @@ import javax.persistence.TemporalType;
import org.hibernate.QueryException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.model.domain.AllowableTemporalParameterType;
import org.hibernate.query.CastType;
import org.hibernate.type.descriptor.java.OffsetDateTimeJavaDescriptor;
import org.hibernate.type.descriptor.sql.TimestampWithTimeZoneDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
@ -79,4 +80,9 @@ public class OffsetDateTimeType
}
}
}
@Override
public CastType getCastType() {
return CastType.OFFSET_TIMESTAMP;
}
}

View File

@ -9,10 +9,9 @@ package org.hibernate.type;
import java.io.Serializable;
import org.hibernate.dialect.Dialect;
import org.hibernate.query.CastType;
import org.hibernate.type.descriptor.java.BooleanTypeDescriptor;
import org.hibernate.type.descriptor.sql.CharTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
/**
* A type that maps between {@link java.sql.Types#CHAR CHAR(1)} and {@link Boolean} (using 'T' and 'F')
@ -22,7 +21,7 @@ import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
*/
public class TrueFalseType
extends AbstractSingleColumnStandardBasicType<Boolean>
implements PrimitiveType<Boolean>, DiscriminatorType<Boolean>, SqlTypeDescriptorIndicatorCapable<Boolean> {
implements PrimitiveType<Boolean>, DiscriminatorType<Boolean> {
public static final TrueFalseType INSTANCE = new TrueFalseType();
@ -51,18 +50,7 @@ public class TrueFalseType
}
@Override
public <X> BasicType<X> resolveIndicatedType(SqlTypeDescriptorIndicators indicators) {
if ( indicators.getPreferredSqlTypeCodeForBoolean() != getSqlTypeDescriptor().getJdbcTypeCode() ) {
final SqlTypeDescriptor sqlTypeDescriptor = indicators.getTypeConfiguration()
.getSqlTypeDescriptorRegistry()
.getDescriptor( indicators.getPreferredSqlTypeCodeForBoolean() );
//noinspection unchecked
return (BasicType<X>) indicators.getTypeConfiguration()
.getBasicTypeRegistry()
.resolve( getJavaTypeDescriptor(), sqlTypeDescriptor );
}
//noinspection unchecked
return (BasicType<X>) this;
public CastType getCastType() {
return CastType.TF_BOOLEAN;
}
}

View File

@ -9,10 +9,9 @@ package org.hibernate.type;
import java.io.Serializable;
import org.hibernate.dialect.Dialect;
import org.hibernate.query.CastType;
import org.hibernate.type.descriptor.java.BooleanTypeDescriptor;
import org.hibernate.type.descriptor.sql.CharTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
/**
* A type that maps between {@link java.sql.Types#CHAR CHAR(1)} and {@link Boolean} (using 'Y' and 'N')
@ -22,7 +21,7 @@ import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
*/
public class YesNoType
extends AbstractSingleColumnStandardBasicType<Boolean>
implements PrimitiveType<Boolean>, DiscriminatorType<Boolean>, SqlTypeDescriptorIndicatorCapable<Boolean> {
implements PrimitiveType<Boolean>, DiscriminatorType<Boolean> {
public static final YesNoType INSTANCE = new YesNoType();
@ -51,18 +50,7 @@ public class YesNoType
}
@Override
public <X> BasicType<X> resolveIndicatedType(SqlTypeDescriptorIndicators indicators) {
if ( indicators.getPreferredSqlTypeCodeForBoolean() != getSqlTypeDescriptor().getJdbcTypeCode() ) {
final SqlTypeDescriptor sqlTypeDescriptor = indicators.getTypeConfiguration()
.getSqlTypeDescriptorRegistry()
.getDescriptor( indicators.getPreferredSqlTypeCodeForBoolean() );
//noinspection unchecked
return (BasicType<X>) indicators.getTypeConfiguration()
.getBasicTypeRegistry()
.resolve( getJavaTypeDescriptor(), sqlTypeDescriptor );
}
//noinspection unchecked
return (BasicType<X>) this;
public CastType getCastType() {
return CastType.YN_BOOLEAN;
}
}

View File

@ -14,6 +14,7 @@ import org.hibernate.QueryException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.ZonedDateTimeComparator;
import org.hibernate.metamodel.model.domain.AllowableTemporalParameterType;
import org.hibernate.query.CastType;
import org.hibernate.type.descriptor.java.ZonedDateTimeJavaDescriptor;
import org.hibernate.type.descriptor.sql.TimestampWithTimeZoneDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
@ -73,4 +74,9 @@ public class ZonedDateTimeType
}
}
}
@Override
public CastType getCastType() {
return CastType.ZONE_TIMESTAMP;
}
}

View File

@ -7,7 +7,9 @@
package org.hibernate.type.descriptor.sql;
import java.io.Serializable;
import java.sql.Types;
import org.hibernate.query.CastType;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
@ -92,4 +94,44 @@ public interface SqlTypeDescriptor extends Serializable {
* @return The appropriate extractor
*/
<X> ValueExtractor<X> getExtractor(JavaTypeDescriptor<X> javaTypeDescriptor);
default CastType getCastType() {
switch ( getSqlType() ) {
case Types.INTEGER:
case Types.TINYINT:
case Types.SMALLINT:
return CastType.INTEGER;
case Types.BIGINT:
return CastType.LONG;
case Types.FLOAT:
case Types.REAL:
return CastType.FLOAT;
case Types.DOUBLE:
return CastType.DOUBLE;
case Types.CHAR:
case Types.NCHAR:
case Types.VARCHAR:
case Types.NVARCHAR:
case Types.LONGVARCHAR:
case Types.LONGNVARCHAR:
return CastType.STRING;
case Types.BOOLEAN:
return CastType.BOOLEAN;
case Types.DECIMAL:
case Types.NUMERIC:
return CastType.FIXED;
case Types.DATE:
return CastType.DATE;
case Types.TIME:
return CastType.TIME;
case Types.TIMESTAMP:
return CastType.TIMESTAMP;
case Types.TIMESTAMP_WITH_TIMEZONE:
return CastType.OFFSET_TIMESTAMP;
case Types.NULL:
return CastType.NULL;
default:
return CastType.OTHER;
}
}
}

View File

@ -46,7 +46,7 @@ public class VarbinaryTypeDescriptor implements SqlTypeDescriptor {
@Override
public String toString() {
return "VarbinaryTypeDescriptor)";
return "VarbinaryTypeDescriptor";
}
public int getSqlType() {

View File

@ -6,10 +6,14 @@
*/
package org.hibernate.type.internal;
import java.sql.Types;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.query.CastType;
import org.hibernate.type.AbstractSingleColumnStandardBasicType;
import org.hibernate.type.BasicType;
import org.hibernate.type.SqlTypeDescriptorIndicatorCapable;
import org.hibernate.type.descriptor.java.BooleanTypeDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
@ -51,4 +55,20 @@ public class StandardBasicTypeImpl<J>
.getBasicTypeRegistry()
.resolve( getJavaTypeDescriptor(), recommendedSqlType );
}
@Override
public CastType getCastType() {
if ( getJavaTypeDescriptor() == BooleanTypeDescriptor.INSTANCE ) {
switch ( sqlType() ) {
case Types.BIT:
case Types.SMALLINT:
case Types.TINYINT:
case Types.INTEGER:
return CastType.INTEGER_BOOLEAN;
case Types.CHAR:
return CastType.YN_BOOLEAN;
}
}
return super.getCastType();
}
}

View File

@ -285,6 +285,9 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable {
//this one is very fragile ... works well for BIT or BOOLEAN columns only
//works OK, I suppose, for integer columns, but not at all for char columns
case "boolean": return getBasicTypeForJavaType( Boolean.class );
case "truefalse": return StandardBasicTypes.TRUE_FALSE;
case "yesno": return StandardBasicTypes.YES_NO;
case "numericboolean": return StandardBasicTypes.NUMERIC_BOOLEAN;
default: throw new HibernateException( "unrecognized cast target type: " + name );
}
}

View File

@ -2,6 +2,7 @@ package org.hibernate.orm.test.annotations.collectionelement;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
@ -100,6 +101,7 @@ public class EmbeddableElementCollectionMemberOfTest {
this.street = street;
}
@Column(name = "house_number")
public int getNumber() {
return number;
}

View File

@ -6,16 +6,11 @@
*/
package org.hibernate.orm.test.columntransformer;
import java.util.Collections;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.graph.GraphParser;
import org.hibernate.graph.GraphSemantic;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
@ -122,7 +117,7 @@ public class ColumnTransformerTest {
final String sqlString =
// represents how each is mapped in the mappings - see their @ColumnTransformer#read
"select size_in_cm / 2.54E0"
+ ", radiusS / 2.54d"
+ ", radiusS / 2.54E0"
+ ", diamet / 2.54E0"
+ " from t_staff"
+ " where t_staff.id = 4";

View File

@ -18,9 +18,9 @@ import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.DerbyDialect;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.HSQLDialect;
import org.hibernate.dialect.MariaDB103Dialect;
import org.hibernate.dialect.Oracle8iDialect;
import org.hibernate.dialect.SQLServer2012Dialect;
import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.dialect.OracleDialect;
import org.hibernate.dialect.SQLServerDialect;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.exception.GenericJDBCException;
import org.hibernate.jdbc.Work;
@ -41,10 +41,10 @@ public class SequenceValueExtractor {
else if ( dialect instanceof DB2Dialect ) {
queryString = "values PREVIOUS value for " + sequenceName;
}
else if ( dialect instanceof Oracle8iDialect ) {
else if ( dialect instanceof OracleDialect && dialect.getVersion() >= 8 ) {
queryString = "select " + sequenceName + ".currval from dual";
}
else if ( dialect instanceof SQLServer2012Dialect ) {
else if ( dialect instanceof SQLServerDialect && dialect.getVersion() >= 11 ) {
queryString = "SELECT CONVERT(varchar(200), Current_value) FROM sys.sequences WHERE name = '" + sequenceName + "'";
}
else if ( dialect instanceof HSQLDialect ) {
@ -55,7 +55,7 @@ public class SequenceValueExtractor {
queryString = "select " + sequenceName + ".currval from sys.dummy";
}
else if ( dialect instanceof MariaDB103Dialect ) {
else if ( dialect instanceof MariaDBDialect && dialect.getVersion() >= 1030 ) {
queryString = "select LASTVAL(" + sequenceName + ")";
}

View File

@ -75,9 +75,13 @@ public class SequenceMismatchStrategyFixWithSequenceGeneratorTest extends Entity
@AfterAll
public void releaseResources() {
if ( metadata != null ) {
new SchemaExport().drop( EnumSet.of( TargetType.DATABASE ), metadata );
}
if ( serviceRegistry != null ) {
StandardServiceRegistryBuilder.destroy( serviceRegistry );
}
}
@Override
protected Class<?>[] getAnnotatedClasses() {

View File

@ -73,9 +73,13 @@ public class SequenceMismatchStrategyLogTest extends EntityManagerFactoryBasedFu
@AfterAll
public void releaseResources() {
if ( metadata != null ) {
new SchemaExport().drop( EnumSet.of( TargetType.DATABASE ), metadata );
}
if ( serviceRegistry != null ) {
StandardServiceRegistryBuilder.destroy( serviceRegistry );
}
}
@Override
protected Class<?>[] getAnnotatedClasses() {

View File

@ -74,9 +74,13 @@ public class SequenceMismatchStrategyWithoutSequenceGeneratorTest extends Entity
@AfterAll
public void releaseResources() {
if ( metadata != null ) {
new SchemaExport().drop( EnumSet.of( TargetType.DATABASE ), metadata );
}
if ( serviceRegistry != null ) {
StandardServiceRegistryBuilder.destroy( serviceRegistry );
}
}
@Override
protected Class<?>[] getAnnotatedClasses() {

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.orm.test.mapping.naturalid.inheritance;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
@ -24,6 +25,7 @@ public class User extends Principal {
super( uid );
}
@Column(name = "user_level")
public String getLevel() {
return level;
}

View File

@ -5,6 +5,7 @@ import java.util.Objects;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@ -135,6 +136,7 @@ public class SortNaturalByDefaultTests {
@GeneratedValue
private Long id;
@Column(name = "phone_number")
private String number;
public Phone() {

View File

@ -65,8 +65,10 @@ public class OneToManySelfReferenceTest {
@AfterEach
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session ->
session.createQuery( "delete from Event" ).executeUpdate()
session -> {
session.createQuery( "update Event e set e.parent = null" ).executeUpdate();
session.createQuery( "delete from Event" ).executeUpdate();
}
);
}

View File

@ -6,24 +6,20 @@
*/
package org.hibernate.orm.test.query.hql;
import org.hibernate.boot.MetadataSources;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.dialect.DerbyDialect;
import org.hibernate.testing.DialectChecks;
import org.hibernate.testing.junit5.SessionFactoryBasedFunctionalTest;
import org.hibernate.testing.orm.domain.StandardDomainModel;
import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
import org.hibernate.testing.orm.junit.DialectFeatureCheck;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.SkipForDialect;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import javax.persistence.EntityManager;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
@ -43,25 +39,24 @@ import static org.hamcrest.MatcherAssert.assertThat;
@ServiceRegistry
@DomainModel( standardModels = StandardDomainModel.GAMBIT )
@SessionFactory
public class FunctionTests extends SessionFactoryBasedFunctionalTest {
public class FunctionTests {
@Override
protected void applyMetadataSources(MetadataSources metadataSources) {
StandardDomainModel.GAMBIT.getDescriptor().applyDomainModel(metadataSources);
}
@Override
protected void sessionFactoryBuilt(SessionFactoryImplementor factory) {
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
@BeforeAll
public void prepareData(SessionFactoryScope scope) {
scope.inTransaction(
em -> {
EntityOfBasics entity = new EntityOfBasics();
entity.setId(12);
entity.setId(123);
entity.setTheDate( new Date( 74, 2, 25 ) );
entity.setTheTime( new Time( 23, 10, 8 ) );
entity.setTheTimestamp( new Timestamp( System.currentTimeMillis() ) );
em.persist(entity);
em.getTransaction().commit();
em.close();
}
);
}
@Test
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsCharCodeConversion.class)
public void testAsciiChrFunctions(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
@ -111,7 +106,7 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest {
public void testCoalesceFunction(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery("select coalesce(null, e.gender, e.convertedGender) from EntityOfBasics e")
session.createQuery("select coalesce(nullif('',''), e.gender, e.convertedGender) from EntityOfBasics e")
.list();
session.createQuery("select ifnull(e.gender, e.convertedGender) from EntityOfBasics e")
.list();
@ -159,7 +154,7 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest {
.list();
session.createQuery("select exp(e.theDouble), ln(e.theDouble + 1) from EntityOfBasics e")
.list();
session.createQuery("select power(e.theDouble, 2.5) from EntityOfBasics e")
session.createQuery("select power(e.theDouble + 1, 2.5) from EntityOfBasics e")
.list();
session.createQuery("select ceiling(e.theDouble), floor(e.theDouble) from EntityOfBasics e")
.list();
@ -269,7 +264,7 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest {
}
@Test
@FailureExpected(reason = "needs indirection in positional parameter bindings")
@SkipForDialect(dialectClass = DerbyDialect.class, reason = "Derby doesn't support a parameter in the 'length' function or the 'char' function which we render as emulation")
public void testOverlayFunctionParameters(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
@ -295,6 +290,7 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest {
}
@Test
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsReplace.class)
public void testReplaceFunction(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
@ -342,6 +338,7 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest {
}
@Test
@SkipForDialect(dialectClass = DerbyDialect.class, reason = "Derby doesn't support a parameter in the 'length' function or the 'char' function which we render as emulation")
public void testPadFunctionParameters(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
@ -361,6 +358,10 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest {
public void testCastFunction(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
assertThat( ((String) session.createQuery("select cast(e.theBoolean as String) from EntityOfBasics e").getSingleResult()).toLowerCase(), is("false") );
assertThat( ((String) session.createQuery("select cast(e.theNumericBoolean as String) from EntityOfBasics e").getSingleResult()).toLowerCase(), is("false") );
assertThat( ((String) session.createQuery("select cast(e.theStringBoolean as String) from EntityOfBasics e").getSingleResult()).toLowerCase(), is("false") );
session.createQuery("select cast(e.theDate as String), cast(e.theTime as String), cast(e.theTimestamp as String) from EntityOfBasics e")
.list();
session.createQuery("select cast(e.id as String), cast(e.theInt as String), cast(e.theDouble as String) from EntityOfBasics e")
@ -371,10 +372,6 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest {
.list();
session.createQuery("select cast(e.theString as String(15)), cast(e.theDouble as String(8)) from EntityOfBasics e")
.list();
session.createQuery("select cast(e.theString as Binary) from EntityOfBasics e")
.list();
session.createQuery("select cast(e.theString as Binary(10)) from EntityOfBasics e")
.list();
session.createQuery("select cast('1002342345234523.452435245245243' as BigDecimal) from EntityOfBasics")
.list();
@ -427,6 +424,34 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest {
assertThat( session.createQuery("select cast(date 1911-10-09 as String)").getSingleResult(), is("1911-10-09") );
assertThat( session.createQuery("select cast(time 12:13:14 as String)").getSingleResult(), is("12:13:14") );
assertThat( (String) session.createQuery("select cast(datetime 1911-10-09 12:13:14 as String)").getSingleResult(), startsWith("1911-10-09 12:13:14") );
assertThat( session.createQuery("select cast(1 as NumericBoolean)").getSingleResult(), is(true) );
assertThat( session.createQuery("select cast(0 as NumericBoolean)").getSingleResult(), is(false) );
assertThat( session.createQuery("select cast(true as YesNo)").getSingleResult(), is(true) );
assertThat( session.createQuery("select cast(false as YesNo)").getSingleResult(), is(false) );
assertThat( session.createQuery("select cast(1 as YesNo)").getSingleResult(), is(true) );
assertThat( session.createQuery("select cast(0 as YesNo)").getSingleResult(), is(false) );
assertThat( session.createQuery("select cast(true as TrueFalse)").getSingleResult(), is(true) );
assertThat( session.createQuery("select cast(false as TrueFalse)").getSingleResult(), is(false) );
assertThat( session.createQuery("select cast(1 as TrueFalse)").getSingleResult(), is(true) );
assertThat( session.createQuery("select cast(0 as TrueFalse)").getSingleResult(), is(false) );
assertThat( session.createQuery("select cast('Y' as YesNo)").getSingleResult(), is(true) );
assertThat( session.createQuery("select cast('N' as YesNo)").getSingleResult(), is(false) );
assertThat( session.createQuery("select cast('T' as TrueFalse)").getSingleResult(), is(true) );
assertThat( session.createQuery("select cast('F' as TrueFalse)").getSingleResult(), is(false) );
}
);
}
@Test
@SkipForDialect(dialectClass = DerbyDialect.class, reason = "Derby doesn't support casting to the binary types")
public void testCastFunctionBinary(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery("select cast(e.theString as Binary) from EntityOfBasics e")
.list();
session.createQuery("select cast(e.theString as Binary(10)) from EntityOfBasics e")
.list();
}
);
}
@ -515,7 +540,9 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest {
session -> {
session.createQuery("select avg(e.theDouble), avg(abs(e.theDouble)), min(e.theDouble), max(e.theDouble), sum(e.theDouble), sum(e.theInt) from EntityOfBasics e")
.list();
session.createQuery("select avg(distinct e.theInt), sum(distinct e.theInt) from EntityOfBasics e")
session.createQuery("select avg(distinct e.theInt) from EntityOfBasics e")
.list();
session.createQuery("select sum(distinct e.theInt) from EntityOfBasics e")
.list();
session.createQuery("select any(e.theInt > 0), every(e.theInt > 0) from EntityOfBasics e")
.list();
@ -763,11 +790,7 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest {
.list();
session.createQuery("select extract(day of month from e.theDate) from EntityOfBasics e")
.list();
session.createQuery("select extract(day of week from e.theDate) from EntityOfBasics e")
.list();
session.createQuery("select extract(week from e.theDate) from EntityOfBasics e")
.list();
session.createQuery("select extract(quarter from e.theDate) from EntityOfBasics e")
.list();
@ -813,6 +836,20 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest {
);
}
@Test
public void testExtractFunctionWeek(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery("select extract(day of week from e.theDate) from EntityOfBasics e")
.list();
session.createQuery("select extract(week from e.theDate) from EntityOfBasics e")
.list();
}
);
}
@Test
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsTimezoneTypes.class)
public void testExtractFunctionTimeZone(SessionFactoryScope scope) {
@ -833,10 +870,6 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest {
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)
@ -932,23 +965,15 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest {
session.createQuery("select extract(time from local datetime) from EntityOfBasics").getResultList().get(0),
instanceOf(LocalTime.class)
);
session.delete(entity);
}
);
}
@Test
@SkipForDialect(dialectClass = DerbyDialect.class, reason = "Derby doesn't support formatting temporal types to strings")
public void testFormat(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
EntityOfBasics entity = new EntityOfBasics();
entity.setId(123);
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")
@ -969,8 +994,8 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest {
}
@Test
public void testGrouping() {
inTransaction(
public void testGrouping(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery("select max(e.theDouble), e.gender, e.theInt from EntityOfBasics e group by e.gender, e.theInt")
.list();
@ -980,12 +1005,21 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest {
@Test
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsGroupByRollup.class)
public void testGroupingFunctions() {
inTransaction(
public void testGroupByRollup(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery("select avg(e.theDouble), e.gender, e.theInt from EntityOfBasics e group by rollup(e.gender, e.theInt)")
.list();
session.createQuery("select sum(e.theDouble), e.gender, e.theInt from EntityOfBasics e group by cube(e.gender, e.theInt)")
}
);
}
@Test
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsGroupByGroupingSets.class)
public void testGroupByGroupingSets(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery("select avg(e.theDouble), e.gender, e.theInt from EntityOfBasics e group by cube(e.gender, e.theInt)")
.list();
}
);

View File

@ -12,6 +12,8 @@ import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalTime;
import org.hibernate.dialect.DerbyDialect;
import org.hibernate.testing.orm.domain.StandardDomainModel;
import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
@ -20,6 +22,8 @@ import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.SkipForDialect;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.instanceOf;
@ -34,6 +38,21 @@ import static org.hamcrest.MatcherAssert.assertThat;
@DomainModel( standardModels = StandardDomainModel.GAMBIT )
@SessionFactory
public class StandardFunctionTests {
@BeforeAll
public void prepareData(SessionFactoryScope scope) {
scope.inTransaction(
em -> {
EntityOfBasics entity = new EntityOfBasics();
entity.setId(123);
entity.setTheDate( new Date( 74, 2, 25 ) );
entity.setTheTime( new Time( 23, 10, 8 ) );
entity.setTheTimestamp( new Timestamp( System.currentTimeMillis() ) );
em.persist(entity);
}
);
}
@Test
public void currentTimestampTests(SessionFactoryScope scope) {
scope.inTransaction(
@ -105,11 +124,11 @@ public class StandardFunctionTests {
session.createQuery( "select local_date from EntityOfBasics" ).list();
session.createQuery( "select local_date() from EntityOfBasics" ).list();
session.createQuery( "select e from EntityOfBasics e where e.theTimestamp = local_date" ).list();
session.createQuery( "select e from EntityOfBasics e where e.theTimestamp = local_date()" ).list();
session.createQuery( "select e from EntityOfBasics e where e.theDate = local_date" ).list();
session.createQuery( "select e from EntityOfBasics e where e.theDate = local_date()" ).list();
session.createQuery( "select e from EntityOfBasics e where local_date between e.theTimestamp and e.theTimestamp" ).list();
session.createQuery( "select e from EntityOfBasics e where local_date() between e.theTimestamp and e.theTimestamp" ).list();
session.createQuery( "select e from EntityOfBasics e where local_date between e.theDate and e.theDate" ).list();
session.createQuery( "select e from EntityOfBasics e where local_date() between e.theDate and e.theDate" ).list();
assertThat(
session.createQuery( "select local_date" ).getSingleResult(),
@ -154,7 +173,7 @@ public class StandardFunctionTests {
public void testCoalesceFunction(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery("select coalesce(null, e.gender, e.convertedGender) from EntityOfBasics e")
session.createQuery("select coalesce(nullif('',''), e.gender, e.convertedGender) from EntityOfBasics e")
.list();
session.createQuery("select ifnull(e.gender, e.convertedGender) from EntityOfBasics e")
.list();
@ -191,9 +210,9 @@ public class StandardFunctionTests {
.list();
session.createQuery("select abs(e.theDouble), sign(e.theDouble), sqrt(e.theDouble) from EntityOfBasics e")
.list();
session.createQuery("select exp(e.theDouble), ln(e.theDouble) from EntityOfBasics e")
session.createQuery("select exp(e.theDouble), ln(e.theDouble + 1) from EntityOfBasics e")
.list();
session.createQuery("select power(e.theDouble, 2.5) from EntityOfBasics e")
session.createQuery("select power(e.theDouble + 1, 2.5) from EntityOfBasics e")
.list();
session.createQuery("select ceiling(e.theDouble), floor(e.theDouble) from EntityOfBasics e")
.list();
@ -232,6 +251,7 @@ public class StandardFunctionTests {
}
@Test
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsCharCodeConversion.class)
public void testAsciiChrFunctions(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
@ -311,6 +331,7 @@ public class StandardFunctionTests {
}
@Test
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsReplace.class)
public void testReplaceFunction(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
@ -612,14 +633,14 @@ public class StandardFunctionTests {
public void testExtractFunctionTimeZone(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery("select extract(offset hour from e.theTime) from EntityOfBasics e")
session.createQuery("select extract(offset hour from e.theZonedDateTime) from EntityOfBasics e")
.list();
// the grammar rule is defined as `HOUR | MINUTE` so no idea how both ever worked.
// session.createQuery("select extract(offset hour minute from e.theTime) from EntityOfBasics e")
// session.createQuery("select extract(offset hour minute from e.theZonedDateTime) from EntityOfBasics e")
// .list();
session.createQuery("select extract(offset from e.theTimestamp) from EntityOfBasics e")
session.createQuery("select extract(offset from e.theZonedDateTime) from EntityOfBasics e")
.list();
}
);
@ -638,20 +659,11 @@ public class StandardFunctionTests {
@Test
// @FailureExpected
public void testExtractFunctionWithAssertions(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
EntityOfBasics entity = new EntityOfBasics();
entity.setId(1);
session.save(entity);
}
);
try {
scope.inTransaction(
session -> {
assertThat(
session.createQuery(
"select extract(week of year from {2019-01-01}) from EntityOfBasics b where b.id = 1" )
"select extract(week of year from {2019-01-01}) from EntityOfBasics b where b.id = 123" )
.getResultList()
.get( 0 ),
is( 1 )
@ -791,14 +803,6 @@ public class StandardFunctionTests {
}
);
}
finally {
scope.inTransaction(
session -> {
session.createQuery( "delete from EntityOfBasics" ).executeUpdate();
}
);
}
}
@Test
public void testExtractFunctions(SessionFactoryScope scope) {
@ -874,26 +878,17 @@ public class StandardFunctionTests {
session -> {
session.createQuery("select avg(e.theDouble), avg(abs(e.theDouble)), min(e.theDouble), max(e.theDouble), sum(e.theDouble), sum(e.theInt) from EntityOfBasics e")
.list();
session.createQuery("select avg(distinct e.theInt), sum(distinct e.theInt) from EntityOfBasics e")
session.createQuery("select sum(distinct e.theInt) from EntityOfBasics e")
.list();
session.createQuery("select sum(distinct e.theInt) from EntityOfBasics e")
.list();
}
);
}
@Test
@SkipForDialect(dialectClass = DerbyDialect.class, reason = "Derby doesn't support formatting temporal types to strings")
public void testFormat(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
EntityOfBasics entity = new EntityOfBasics();
entity.setId(123);
entity.setTheDate( new Date( 74, 2, 25 ) );
entity.setTheTime( new Time( 23, 10, 8 ) );
entity.setTheTimestamp( new Timestamp( System.currentTimeMillis() ) );
session.persist( entity );
}
);
try {
scope.inTransaction(
session -> {
session.createQuery( "select format(e.theTime as 'hh:mm:ss aa') from EntityOfBasics e" )
@ -922,12 +917,4 @@ public class StandardFunctionTests {
}
);
}
finally {
scope.inTransaction(
session -> {
session.createQuery( "delete EntityOfBasics" ).executeUpdate();
}
);
}
}
}

View File

@ -35,7 +35,7 @@ public class NativeQueryParameterTests {
public void testBasicParameterBinding(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createNativeQuery( "select t.id, t.ticket_key, t.subject from ticket t where t.ticket_key = ?" )
session.createNativeQuery( "select t.id, t.ticket_key, t.subject from Ticket t where t.ticket_key = ?" )
.setParameter( 1, "ABC-123" )
.list();
}
@ -45,7 +45,7 @@ public class NativeQueryParameterTests {
@Test
public void testJpaStylePositionalParametersInNativeSql(SessionFactoryScope scope) {
scope.inTransaction(
s -> s.createNativeQuery( "select t.subject from ticket t where t.ticket_key = ?1" ).setParameter( 1, "ABC-123" ).list()
s -> s.createNativeQuery( "select t.subject from Ticket t where t.ticket_key = ?1" ).setParameter( 1, "ABC-123" ).list()
);
}
@ -53,7 +53,7 @@ public class NativeQueryParameterTests {
public void testTypedParameterBinding(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createNativeQuery( "select t.id, t.ticket_key, t.subject from ticket t where t.ticket_key = ?" )
session.createNativeQuery( "select t.id, t.ticket_key, t.subject from Ticket t where t.ticket_key = ?" )
.setParameter( 1, "ABC-123", StandardBasicTypes.STRING )
.list();
}
@ -63,7 +63,7 @@ public class NativeQueryParameterTests {
@Test
public void testTemporalParameterBinding(SessionFactoryScope scope) {
final String qryString = "select i.id, i.effectiveStart, i.effectiveEnd " +
" from incident i" +
" from Incident i" +
" where i.reported BETWEEN ? AND ?";
scope.inTransaction(

View File

@ -14,6 +14,7 @@ import java.util.List;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.DerbyDialect;
import org.hibernate.dialect.SQLServerDialect;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.BasicValuedSingularAttributeMapping;
@ -80,10 +81,11 @@ public class NativeQueryResultBuilderTests {
public void fullyImplicitTest2(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
// DB2 and Derby return an Integer for count by default
// DB2, Derby and SQL Server return an Integer for count by default
Assumptions.assumeThat( session.getJdbcServices().getDialect() )
.isNotInstanceOf( DB2Dialect.class )
.isNotInstanceOf( DerbyDialect.class );
.isNotInstanceOf( DerbyDialect.class )
.isNotInstanceOf( SQLServerDialect.class );
final String sql = "select count(theString) from EntityOfBasics";
final NativeQueryImplementor<?> query = session.createNativeQuery( sql );

View File

@ -75,6 +75,23 @@ public class SetOperationTest {
@Test
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsUnion.class)
public void testUnionAllLimit(SessionFactoryScope scope) {
scope.inSession(
session -> {
List<Tuple> list = session.createQuery(
"(select e.id, e from EntityOfLists e where e.id = 1 " +
"union all " +
"select e.id, e from EntityOfLists e where e.id = 2) " +
"order by 1 fetch first 1 row only",
Tuple.class
).list();
assertThat( list.size(), is( 1 ) );
}
);
}
@Test
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsUnion.class)
public void testUnionAllLimitSubquery(SessionFactoryScope scope) {
scope.inSession(
session -> {
List<Tuple> list = session.createQuery(
@ -84,6 +101,23 @@ public class SetOperationTest {
"order by 1 fetch first 1 row only",
Tuple.class
).list();
assertThat( list.size(), is( 2 ) );
}
);
}
@Test
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsUnion.class)
public void testUnionAllLimitNested(SessionFactoryScope scope) {
scope.inSession(
session -> {
List<Tuple> list = session.createQuery(
"(select e.id, e from EntityOfLists e where e.id = 1 " +
"union all " +
"(select e.id, e from EntityOfLists e where e.id = 2 order by 1 fetch first 1 row only)) " +
"order by 1 fetch first 1 row only",
Tuple.class
).list();
assertThat( list.size(), is( 1 ) );
}
);

View File

@ -120,9 +120,13 @@ public class SmokeTests {
sqlAst
).translate( null, QueryOptions.NONE );
final String separator = session.getSessionFactory()
.getJdbcServices()
.getDialect()
.getTableAliasSeparator();
assertThat(
jdbcSelectOperation.getSql(),
is( "select s1_0.name from mapping_simple_entity as s1_0" )
is( "select s1_0.name from mapping_simple_entity" + separator + "s1_0" )
);
}
);
@ -216,9 +220,13 @@ public class SmokeTests {
sqlAst
).translate( null, QueryOptions.NONE );
final String separator = session.getSessionFactory()
.getJdbcServices()
.getDialect()
.getTableAliasSeparator();
assertThat(
jdbcSelectOperation.getSql(),
is( "select s1_0.gender from mapping_simple_entity as s1_0" )
is( "select s1_0.gender from mapping_simple_entity" + separator + "s1_0" )
);
}
);

View File

@ -67,6 +67,7 @@ public class EntityWithLazyManyToOneSelfReferenceTest {
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "update EntityWithLazyManyToOneSelfReference e set e.other = null" ).executeUpdate();
session.createQuery( "delete from EntityWithLazyManyToOneSelfReference" ).executeUpdate();
}
);

View File

@ -66,6 +66,7 @@ public class EntityWithManyToOneSelfReferenceTest {
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "update EntityWithManyToOneSelfReference e set e.other = null" ).executeUpdate();
session.createQuery( "delete from EntityWithManyToOneSelfReference" ).executeUpdate();
}
);

View File

@ -0,0 +1,59 @@
/*
* 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.testing.junit5;
import java.lang.reflect.Constructor;
import java.sql.Connection;
import java.sql.Driver;
import java.util.Properties;
import org.hibernate.HibernateException;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.dialect.spi.DatabaseMetaDataDialectResolutionInfoAdapter;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.internal.util.ReflectHelper;
/**
* @author Christian Beikov
*/
public final class DialectContext {
private static final Dialect dialect;
static {
final Properties properties = Environment.getProperties();
final String dialectName = properties.getProperty( Environment.DIALECT );
if ( dialectName == null ) {
throw new HibernateException( "The dialect was not set. Set the property hibernate.dialect." );
}
try {
final Class<? extends Dialect> dialectClass = ReflectHelper.classForName( dialectName );
final Constructor<? extends Dialect> constructor = dialectClass.getConstructor( DialectResolutionInfo.class );
Driver driver = (Driver) Class.forName( properties.getProperty( Environment.DRIVER ) ).newInstance();
Properties props = new Properties();
props.setProperty( "user", properties.getProperty( Environment.USER ) );
props.setProperty( "password", properties.getProperty( Environment.PASS ) );
try (Connection connection = driver.connect( properties.getProperty( Environment.URL ), props )) {
dialect = constructor.newInstance( new DatabaseMetaDataDialectResolutionInfoAdapter( connection.getMetaData() ) );
}
}
catch (ClassNotFoundException cnfe) {
throw new HibernateException( "Dialect class not found: " + dialectName );
}
catch (Exception e) {
throw new HibernateException( "Could not instantiate given dialect class: " + dialectName, e );
}
}
private DialectContext() {
}
public static Dialect getDialect() {
return dialect;
}
}

View File

@ -37,16 +37,6 @@ public class DialectFilterExtension implements ExecutionCondition {
@Override
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
if ( !context.getTestInstance().isPresent() ) {
assert !context.getTestMethod().isPresent();
return ConditionEvaluationResult.enabled(
"No test-instance was present - " +
"likely that test was not defined with a per-class test lifecycle; " +
"skipping Dialect checks for this context [" + context.getDisplayName() + "]"
);
}
final Dialect dialect = getDialect( context );
if ( dialect == null ) {
throw new RuntimeException( "#getDialect returned null" );
@ -135,7 +125,7 @@ public class DialectFilterExtension implements ExecutionCondition {
try {
final DialectFeatureCheck dialectFeatureCheck = effectiveRequiresDialectFeature.feature()
.newInstance();
final boolean applicable = dialectFeatureCheck.apply( getDialect( context ) );
final boolean applicable = dialectFeatureCheck.apply( dialect );
final boolean reverse = effectiveRequiresDialectFeature.reverse();
if ( !( applicable ^ reverse ) ) {
return ConditionEvaluationResult.disabled(
@ -156,22 +146,6 @@ public class DialectFilterExtension implements ExecutionCondition {
}
private Dialect getDialect(ExtensionContext context) {
final Optional<SessionFactoryScope> sfScope = SessionFactoryScopeExtension.findSessionFactoryScope( context );
if ( !sfScope.isPresent() ) {
final Optional<EntityManagerFactoryScope> emScope = EntityManagerFactoryScopeExtension.findEntityManagerFactoryScope( context );
if ( !emScope.isPresent() ) {
final Optional<DialectAccess> dialectAccess = Optional.ofNullable(
(DialectAccess) context.getStore( DialectAccess.NAMESPACE )
.get( context.getRequiredTestInstance() ) );
if ( !dialectAccess.isPresent() ) {
throw new RuntimeException(
"Could not locate any DialectAccess implementation in JUnit ExtensionContext" );
}
return dialectAccess.get().getDialect();
}
return emScope.get().getDialect();
}
return sfScope.get().getDialect();
return DialectContext.getDialect();
}
}

View File

@ -9,7 +9,6 @@ package org.hibernate.testing.junit5;
import javax.persistence.EntityManagerFactory;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor;
/**
* Contract for things that expose an EntityManagerFactory
@ -21,6 +20,6 @@ public interface EntityManagerFactoryAccess extends DialectAccess {
@Override
default Dialect getDialect() {
return Dialect.getDialect();
return DialectContext.getDialect();
}
}

View File

@ -19,6 +19,6 @@ public interface SessionFactoryAccess extends DialectAccess {
@Override
default Dialect getDialect() {
return getSessionFactory().getJdbcServices().getDialect();
return DialectContext.getDialect();
}
}

View File

@ -29,6 +29,7 @@ import javax.persistence.SqlResultSetMapping;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.hibernate.annotations.SqlType;
import org.hibernate.annotations.SqlTypeCode;
/**
@ -49,7 +50,9 @@ public class EntityOfBasics {
}
private Integer id;
private Boolean theBoolean;
private Boolean theBoolean = false;
private Boolean theNumericBoolean = false;
private Boolean theStringBoolean = false;
private String theString;
private Integer theInteger;
private int theInt;
@ -258,6 +261,24 @@ public class EntityOfBasics {
this.theBoolean = theBoolean;
}
@SqlTypeCode( Types.INTEGER )
public Boolean isTheNumericBoolean() {
return theNumericBoolean;
}
public void setTheNumericBoolean(Boolean theNumericBoolean) {
this.theNumericBoolean = theNumericBoolean;
}
@SqlTypeCode( Types.CHAR )
public Boolean isTheStringBoolean() {
return theStringBoolean;
}
public void setTheStringBoolean(Boolean theStringBoolean) {
this.theStringBoolean = theStringBoolean;
}
@Convert( converter = MutableValueConverter.class )
public MutableValue getMutableValue() {
return mutableValue;

View File

@ -7,6 +7,7 @@
package org.hibernate.testing.orm.domain.gambit;
import javax.persistence.AttributeConverter;
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.EnumType;
@ -25,6 +26,7 @@ public class Shirt {
private String data;
@Enumerated
@Column(name = "shirt_size")
private Size size;
@Enumerated(EnumType.STRING)

View File

@ -16,6 +16,7 @@ import org.hibernate.dialect.FirebirdDialect;
import org.hibernate.dialect.GroupBySummarizationRenderingStrategy;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.HSQLDialect;
import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.dialect.MaxDBDialect;
import org.hibernate.dialect.MimerSQLDialect;
import org.hibernate.dialect.MySQLDialect;
@ -104,7 +105,7 @@ abstract public class DialectFeatureChecks {
}
}
public static class SupportSubqueryAsLeftHandSideInPredicate implements DialectFeatureCheck {
public static class SupportsSubqueryAsLeftHandSideInPredicate implements DialectFeatureCheck {
public boolean apply(Dialect dialect) {
return dialect.supportsSubselectAsInPredicateLHS();
}
@ -233,7 +234,7 @@ abstract public class DialectFeatureChecks {
public static class SupportsPadWithChar implements DialectFeatureCheck {
public boolean apply(Dialect dialect) {
return !(dialect instanceof DerbyDialect );
return !( dialect instanceof DerbyDialect );
}
}
@ -243,6 +244,17 @@ abstract public class DialectFeatureChecks {
}
}
public static class SupportsGroupByGroupingSets implements DialectFeatureCheck {
public boolean apply(Dialect dialect) {
return dialect.getGroupBySummarizationRenderingStrategy() != GroupBySummarizationRenderingStrategy.NONE
&& !( dialect instanceof DerbyDialect )
// MariaDB only supports ROLLUP
&& !( dialect instanceof MariaDBDialect )
// MySQL only supports ROLLUP
&& !( dialect instanceof MySQLDialect );
}
}
public static class SupportsTimezoneTypes implements DialectFeatureCheck {
public boolean apply(Dialect dialect) {
return dialect.supportsTimezoneTypes();
@ -277,6 +289,7 @@ abstract public class DialectFeatureChecks {
|| dialect instanceof DB2Dialect
|| dialect instanceof FirebirdDialect && dialect.getVersion() >= 300
|| dialect instanceof H2Dialect && dialect.getVersion() >= 104198
|| dialect instanceof MariaDBDialect && dialect.getVersion() >= 1020
|| dialect instanceof MySQLDialect && dialect.getVersion() >= 802
|| dialect instanceof OracleDialect
|| dialect instanceof PostgreSQLDialect
@ -291,4 +304,18 @@ abstract public class DialectFeatureChecks {
}
}
public static class SupportsCharCodeConversion implements DialectFeatureCheck {
public boolean apply(Dialect dialect) {
// Derby doesn't support the `ASCII` or `CHR` functions
return !( dialect instanceof DerbyDialect );
}
}
public static class SupportsReplace implements DialectFeatureCheck {
public boolean apply(Dialect dialect) {
// Derby doesn't support the `REPLACE` function
return !( dialect instanceof DerbyDialect );
}
}
}

View File

@ -9,10 +9,9 @@ package org.hibernate.testing.orm.junit;
import java.util.List;
import java.util.Locale;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.testing.junit5.DialectContext;
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;
@ -30,16 +29,6 @@ public class DialectFilterExtension implements ExecutionCondition {
@Override
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
if ( !context.getTestInstance().isPresent() ) {
assert !context.getTestMethod().isPresent();
return ConditionEvaluationResult.enabled(
"No test-instance was present - " +
"likely that test was not defined with a per-class test lifecycle; " +
"skipping Dialect checks for this context [" + context.getDisplayName() + "]"
);
}
final Dialect dialect = getDialect( context );
if ( dialect == null ) {
throw new RuntimeException( "#getDialect returned null" );
@ -109,7 +98,7 @@ public class DialectFilterExtension implements ExecutionCondition {
try {
final DialectFeatureCheck dialectFeatureCheck = effectiveRequiresDialectFeature.feature()
.newInstance();
if ( !dialectFeatureCheck.apply( getDialect( context ) ) ) {
if ( !dialectFeatureCheck.apply( dialect ) ) {
return ConditionEvaluationResult.disabled(
String.format(
Locale.ROOT,
@ -127,11 +116,6 @@ public class DialectFilterExtension implements ExecutionCondition {
}
private Dialect getDialect(ExtensionContext context) {
final StandardServiceRegistry serviceRegistry = ServiceRegistryExtension.findServiceRegistry(
context.getRequiredTestInstance(),
context
);
return serviceRegistry.getService( JdbcServices.class ).getJdbcEnvironment().getDialect();
return DialectContext.getDialect();
}
}