diff --git a/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 b/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 index c50258de74..e84a23291e 100644 --- a/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 +++ b/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 @@ -277,15 +277,7 @@ mapKeyNavigablePath // GROUP BY clause groupByClause - : GROUP BY groupingSpecification - ; - -groupingSpecification - : groupingValue ( COMMA groupingValue )* - ; - -groupingValue - : expression collationSpecification? + : GROUP BY expression ( COMMA expression )* ; @@ -312,7 +304,7 @@ orderByFragment ; sortSpecification - : sortExpression collationSpecification? orderingSpecification? nullsPrecedence? + : sortExpression orderingSpecification? nullsPrecedence? ; nullsPrecedence @@ -320,8 +312,8 @@ nullsPrecedence ; sortExpression - : identifier - | INTEGER_LITERAL + : identifier collationSpecification? + | INTEGER_LITERAL collationSpecification? | expression ; @@ -404,23 +396,23 @@ likeEscape expression //highest to lowest precedence - : LEFT_PAREN expression RIGHT_PAREN # GroupedExpression - | LEFT_PAREN subQuery RIGHT_PAREN # SubQueryExpression - | caseList # CaseExpression - | literal # LiteralExpression - | parameter # ParameterExpression - | entityTypeReference # EntityTypeExpression - | entityIdReference # EntityIdExpression - | entityVersionReference # EntityVersionExpression - | entityNaturalIdReference # EntityNaturalIdExpression - | path # PathExpression - | function # FunctionExpression - | signOperator expression # UnaryExpression - | expression datetimeField # ToDurationExpression - | expression BY datetimeField # FromDurationExpression - | expression multiplicativeOperator expression # MultiplicationExpression - | expression additiveOperator expression # AdditionExpression - | expression DOUBLE_PIPE expression # ConcatenationExpression + : LEFT_PAREN expression RIGHT_PAREN # GroupedExpression + | LEFT_PAREN subQuery RIGHT_PAREN # SubQueryExpression + | caseList collationSpecification? # CaseExpression + | literal collationSpecification? # LiteralExpression + | parameter collationSpecification? # ParameterExpression + | entityTypeReference # EntityTypeExpression + | entityIdReference collationSpecification? # EntityIdExpression + | entityVersionReference collationSpecification? # EntityVersionExpression + | entityNaturalIdReference collationSpecification? # EntityNaturalIdExpression + | path collationSpecification? # PathExpression + | function collationSpecification? # FunctionExpression + | signOperator expression # UnaryExpression + | expression datetimeField # ToDurationExpression + | expression BY datetimeField # FromDurationExpression + | expression multiplicativeOperator expression # MultiplicationExpression + | expression additiveOperator expression # AdditionExpression + | expression DOUBLE_PIPE expression # ConcatenationExpression ; multiplicativeOperator @@ -568,7 +560,9 @@ day: INTEGER_LITERAL; hour: INTEGER_LITERAL; minute: INTEGER_LITERAL; second: INTEGER_LITERAL | FLOAT_LITERAL; -zoneId: STRING_LITERAL; +zoneId + : IDENTIFIER (SLASH IDENTIFIER)? + | STRING_LITERAL; jdbcTimestampLiteral : TIMESTAMP_ESCAPE_START (dateTime | genericTemporalLiteralText) RIGHT_BRACE diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java index d70b4c8771..99cf3bf918 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -572,6 +572,8 @@ public class DB2Dialect extends Dialect { // Therefore here we overwrite the sql type descriptors to // use the non-N variants which are supported. switch ( sqlCode ) { + case Types.BOOLEAN: + return SmallIntTypeDescriptor.INSTANCE; case Types.NCHAR: return CharTypeDescriptor.INSTANCE; case Types.NCLOB: @@ -678,6 +680,11 @@ public class DB2Dialect extends Dialect { return true; } + @Override + public boolean supportsGroupByRollup() { + return true; + } + @Override public String translateDatetimeFormat(String format) { //DB2 does not need nor support FM diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java index 677021396b..98d3fa089b 100755 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java @@ -9,6 +9,7 @@ package org.hibernate.dialect; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.boot.TempTableDdlTransactionHandling; import org.hibernate.cfg.Environment; +import org.hibernate.dialect.function.CastStrEmulation; import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.function.DerbyConcatEmulation; import org.hibernate.dialect.identity.DB2IdentityColumnSupport; @@ -27,6 +28,7 @@ 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; @@ -41,7 +43,9 @@ import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNo import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.descriptor.sql.DecimalTypeDescriptor; +import org.hibernate.type.descriptor.sql.SmallIntTypeDescriptor; import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; +import org.hibernate.type.descriptor.sql.TimestampTypeDescriptor; import java.sql.DatabaseMetaData; import java.sql.SQLException; @@ -87,11 +91,14 @@ public class DerbyDialect extends Dialect { registerColumnType( Types.BIT, 1, "boolean" ); //no bit registerColumnType( Types.BIT, "smallint" ); //no bit registerColumnType( Types.TINYINT, "smallint" ); //no tinyint + registerColumnType( Types.CHAR, "char(1)" ); //HHH-12827: map them both to the same type to // avoid problems with schema update // registerColumnType( Types.DECIMAL, "decimal($p,$s)" ); registerColumnType( Types.NUMERIC, "decimal($p,$s)" ); + registerColumnType( Types.FLOAT, "float" ); + registerColumnType( Types.DOUBLE, "double" ); registerColumnType( Types.BINARY, "varchar($l) for bit data" ); registerColumnType( Types.BINARY, 254, "char($l) for bit data" ); @@ -165,7 +172,7 @@ public class DerbyDialect extends Dialect { queryEngine.getSqmFunctionRegistry().register( "concat", new DerbyConcatEmulation() ); //no way I can see to pad with anything other than spaces - queryEngine.getSqmFunctionRegistry().patternDescriptorBuilder( "lpad", "case when length(?1) * Basically, does it support syntax like @@ -2590,6 +2591,22 @@ public abstract class Dialect implements ConversionContext { return supportsRowValueConstructorSyntax(); } + /** + * Is this dialect known to support what ANSI-SQL terms "row value + * constructor" syntax; sometimes called tuple syntax with quantified predicates. + *

+ * Basically, does it support syntax like + * "... where (FIRST_NAME, LAST_NAME) = ALL (select ...) ...". + * + * @return True if this SQL dialect is known to support "row value + * constructor" syntax with quantified predicates; false otherwise. + * @since 6.0 + */ + public boolean supportsRowValueConstructorSyntaxInQuantifiedPredicates() { + // return false here, as most databases do not properly support this construct... + return false; + } + /** * If the dialect supports {@link #supportsRowValueConstructorSyntax() row values}, * does it offer such support in IN lists as well? @@ -2604,6 +2621,17 @@ public abstract class Dialect implements ConversionContext { return false; } + /** + * Is this dialect known to support ROLLUP functions in the GROUP BY clause. + * + * @return True if this SQL dialect supports ROLLUP functions; false otherwise. + * @since 6.0 + */ + public boolean supportsGroupByRollup() { + // return false here, as most databases do not properly support this construct... + return false; + } + /** * Should LOBs (both BLOB and CLOB) be bound using stream operations (i.e. * {@link java.sql.PreparedStatement#setBinaryStream}). @@ -3164,6 +3192,14 @@ public abstract class Dialect implements ConversionContext { return String.format( "\'%s\'", escapeLiteral( literal ) ); } + /** + * Appends the collate clause for the given collation name to the SQL appender. + */ + public void appendCollate(SqlAppender sqlAppender, String collationName) { + sqlAppender.appendSql( " collate " ); + sqlAppender.appendSql( collationName ); + } + /** * Check whether the JDBC {@link java.sql.Connection} supports creating LOBs via {@link Connection#createBlob()}, * {@link Connection#createNClob()} or {@link Connection#createClob()}. @@ -3564,66 +3600,73 @@ public abstract class Dialect implements ConversionContext { } protected String wrapTimestampLiteral(String timestamp) { - return wrapAsJdbcTimestampLiteral(timestamp); + return wrapAsJdbcTimestampLiteral( timestamp ); } protected String wrapDateLiteral(String date) { - return wrapAsJdbcDateLiteral(date); + return wrapAsJdbcDateLiteral( date ); } protected String wrapTimeLiteral(String time) { - return wrapAsJdbcTimeLiteral(time); + return wrapAsJdbcTimeLiteral( time ); } - public String formatDateTimeLiteral(TemporalAccessor temporalAccessor, TemporalType precision) { + public String formatDateTimeLiteral( + TemporalAccessor temporalAccessor, + TemporalType precision, + TimeZone jdbcTimeZone) { switch ( precision ) { case DATE: - return wrapDateLiteral( formatAsDate(temporalAccessor) ); + return wrapDateLiteral( formatAsDate( temporalAccessor ) ); case TIME: - return wrapTimeLiteral( formatAsTime(temporalAccessor) ); + return wrapTimeLiteral( formatAsTime( temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ) ); case TIMESTAMP: - return wrapTimestampLiteral( formatAsTimestamp(temporalAccessor) ); + return wrapTimestampLiteral( formatAsTimestamp( temporalAccessor, jdbcTimeZone ) ); default: throw new IllegalArgumentException(); } } - protected String formatAsTimestamp(TemporalAccessor temporalAccessor) { - return formatAsTimestampWithMicros(temporalAccessor); + protected String formatAsTimestamp(TemporalAccessor temporalAccessor, TimeZone jdbcTimeZone) { + return formatAsTimestampWithMicros( temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); } - public String formatDateTimeLiteral(Date date, TemporalType precision) { + public String formatDateTimeLiteral(Date date, TemporalType precision, TimeZone jdbcTimeZone) { switch ( precision ) { case DATE: - return wrapDateLiteral( formatAsDate(date) ); + return wrapDateLiteral( formatAsDate( date ) ); case TIME: - return wrapTimeLiteral( formatAsTime(date) ); + return wrapTimeLiteral( formatAsTime( date ) ); case TIMESTAMP: - return wrapTimestampLiteral( formatAsTimestamp(date) ); + return wrapTimestampLiteral( formatAsTimestamp( date, jdbcTimeZone) ); default: throw new IllegalArgumentException(); } } - protected String formatAsTimestamp(Date date) { - return formatAsTimestampWithMicros(date); + protected String formatAsTimestamp(Date date, TimeZone jdbcTimeZone) { + return formatAsTimestampWithMicros( date, jdbcTimeZone ); } - public String formatDateTimeLiteral(Calendar calendar, TemporalType precision) { + public String formatDateTimeLiteral(Calendar calendar, TemporalType precision, TimeZone jdbcTimeZone) { switch ( precision ) { case DATE: - return wrapDateLiteral( formatAsDate(calendar) ); + return wrapDateLiteral( formatAsDate( calendar ) ); case TIME: - return wrapTimeLiteral( formatAsTime(calendar) ); + return wrapTimeLiteral( formatAsTime( calendar ) ); case TIMESTAMP: - return wrapTimestampLiteral( formatAsTimestamp(calendar) ); + return wrapTimestampLiteral( formatAsTimestamp( calendar, jdbcTimeZone ) ); default: throw new IllegalArgumentException(); } } - protected String formatAsTimestamp(Calendar calendar) { - return formatAsTimestampWithMicros(calendar); + protected String formatAsTimestamp(Calendar calendar, TimeZone jdbcTimeZone) { + return formatAsTimestampWithMicros( calendar, jdbcTimeZone ); + } + + public boolean supportsTemporalLiteralOffset() { + return false; } // deprecated limit/offset support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/FirebirdDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/FirebirdDialect.java index 1b85b87774..303dd3b13b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/FirebirdDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/FirebirdDialect.java @@ -22,6 +22,7 @@ import org.hibernate.engine.jdbc.Size; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.exception.ConstraintViolationException; import org.hibernate.exception.LockAcquisitionException; import org.hibernate.exception.LockTimeoutException; @@ -49,6 +50,7 @@ import java.sql.Types; import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; +import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -491,18 +493,18 @@ public class FirebirdDialect extends Dialect { } @Override - protected String formatAsTimestamp(Date date) { - return formatAsTimestampWithMillis(date); + protected String formatAsTimestamp(Date date, TimeZone jdbcTimeZone) { + return formatAsTimestampWithMillis( date, jdbcTimeZone ); } @Override - protected String formatAsTimestamp(Calendar calendar) { - return formatAsTimestampWithMillis(calendar); + protected String formatAsTimestamp(Calendar calendar, TimeZone jdbcTimeZone) { + return formatAsTimestampWithMillis( calendar, jdbcTimeZone ); } @Override - protected String formatAsTimestamp(TemporalAccessor temporalAccessor) { - return formatAsTimestampWithMillis(temporalAccessor); + protected String formatAsTimestamp(TemporalAccessor temporalAccessor, TimeZone jdbcTimeZone) { + return formatAsTimestampWithMillis( temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java index 118ffcad20..55ba6886ae 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -196,6 +196,11 @@ public class H2Dialect extends Dialect { return "datediff(?1, ?2, ?3)"; } + @Override + public boolean supportsTemporalLiteralOffset() { + return true; + } + @Override public String toBooleanValueString(boolean bool) { return String.valueOf( bool ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java index 702b29876b..60484d1227 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java @@ -918,4 +918,9 @@ public class MySQLDialect extends Dialect { boolean supportsAliasLocks() { return getVersion() >= 800; } + + @Override + public boolean supportsGroupByRollup() { + return true; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index 6ead036d97..e31419a83a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -794,6 +794,11 @@ public class OracleDialect extends Dialect { return false; } + @Override + public boolean supportsGroupByRollup() { + return true; + } + @Override public int getInExpressionCountLimit() { return PARAM_LIST_SIZE_LIMIT; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index 520147ef2a..ccca11b2d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -605,6 +605,11 @@ public class PostgreSQLDialect extends Dialect { return true; } + @Override + public boolean supportsRowValueConstructorSyntaxInQuantifiedPredicates() { + return true; + } + @Override public CallableStatementSupport getCallableStatementSupport() { return PostgresCallableStatementSupport.INSTANCE; @@ -765,6 +770,11 @@ public class PostgreSQLDialect extends Dialect { return getVersion() >= 950; } + @Override + public boolean supportsGroupByRollup() { + return getVersion() >= 950; + } + @Override public void augmentRecognizedTableTypes(List tableTypesList) { super.augmentRecognizedTableTypes( tableTypesList ); @@ -832,7 +842,7 @@ public class PostgreSQLDialect extends Dialect { X value, int index, WrapperOptions wrapperOptions) throws SQLException { - st.setObject( index, javaTypeDescriptor.unwrap( value, UUID.class, wrapperOptions.getSession() ), Types.OTHER ); + st.setObject( index, javaTypeDescriptor.unwrap( value, UUID.class, wrapperOptions ), Types.OTHER ); } @Override @@ -841,7 +851,7 @@ public class PostgreSQLDialect extends Dialect { X value, String name, WrapperOptions wrapperOptions) throws SQLException { - st.setObject( name, javaTypeDescriptor.unwrap( value, UUID.class, wrapperOptions.getSession() ), Types.OTHER ); + st.setObject( name, javaTypeDescriptor.unwrap( value, UUID.class, wrapperOptions ), Types.OTHER ); } }; } @@ -851,17 +861,17 @@ public class PostgreSQLDialect extends Dialect { return new BasicExtractor( javaTypeDescriptor, this ) { @Override protected X doExtract(ResultSet rs, int position, WrapperOptions wrapperOptions) throws SQLException { - return javaTypeDescriptor.wrap( rs.getObject( position ), wrapperOptions.getSession() ); + return javaTypeDescriptor.wrap( rs.getObject( position ), wrapperOptions ); } @Override protected X doExtract(CallableStatement statement, int position, WrapperOptions wrapperOptions) throws SQLException { - return javaTypeDescriptor.wrap( statement.getObject( position ), wrapperOptions.getSession() ); + return javaTypeDescriptor.wrap( statement.getObject( position ), wrapperOptions ); } @Override protected X doExtract(CallableStatement statement, String name, WrapperOptions wrapperOptions) throws SQLException { - return javaTypeDescriptor.wrap( statement.getObject( name ), wrapperOptions.getSession() ); + return javaTypeDescriptor.wrap( statement.getObject( name ), wrapperOptions ); } }; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java index 65ceb23a7e..4f0b19b94a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java @@ -353,6 +353,11 @@ public class SQLServerDialect extends AbstractTransactSQLDialect { return getVersion() >= 9; } + @Override + public boolean supportsGroupByRollup() { + return true; + } + @Override public SequenceSupport getSequenceSupport() { return getVersion() < 11 diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegatorBaseImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegatorBaseImpl.java new file mode 100644 index 0000000000..65a839cb2b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegatorBaseImpl.java @@ -0,0 +1,1108 @@ +/* + * 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 . + */ +package org.hibernate.engine.spi; + +import java.io.Serializable; +import java.sql.Connection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TimeZone; +import java.util.UUID; +import javax.persistence.EntityGraph; +import javax.persistence.EntityManagerFactory; +import javax.persistence.FlushModeType; +import javax.persistence.LockModeType; +import javax.persistence.StoredProcedureQuery; +import javax.persistence.criteria.CriteriaDelete; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.CriteriaUpdate; +import javax.persistence.metamodel.Metamodel; + +import org.hibernate.CacheMode; +import org.hibernate.Filter; +import org.hibernate.FlushMode; +import org.hibernate.HibernateException; +import org.hibernate.IdentifierLoadAccess; +import org.hibernate.Interceptor; +import org.hibernate.LobHelper; +import org.hibernate.LockMode; +import org.hibernate.LockOptions; +import org.hibernate.MultiIdentifierLoadAccess; +import org.hibernate.NaturalIdLoadAccess; +import org.hibernate.NaturalIdMultiLoadAccess; +import org.hibernate.ReplicationMode; +import org.hibernate.Session; +import org.hibernate.SessionEventListener; +import org.hibernate.SharedSessionBuilder; +import org.hibernate.SimpleNaturalIdLoadAccess; +import org.hibernate.Transaction; +import org.hibernate.UnknownProfileException; +import org.hibernate.cache.spi.CacheTransactionSynchronization; +import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.engine.jdbc.LobCreator; +import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; +import org.hibernate.engine.jdbc.spi.JdbcCoordinator; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.query.spi.sql.NativeSQLQuerySpecification; +import org.hibernate.graph.spi.RootGraphImplementor; +import org.hibernate.jdbc.ReturningWork; +import org.hibernate.jdbc.Work; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.procedure.ProcedureCall; +import org.hibernate.query.criteria.HibernateCriteriaBuilder; +import org.hibernate.query.spi.QueryImplementor; +import org.hibernate.query.spi.ScrollableResultsImplementor; +import org.hibernate.query.sql.spi.NativeQueryImplementor; +import org.hibernate.resource.jdbc.spi.JdbcSessionContext; +import org.hibernate.resource.transaction.spi.TransactionCoordinator; +import org.hibernate.stat.SessionStatistics; +import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; + +/** + * + * @author Christian Beikov + */ +public abstract class SessionLazyDelegatorBaseImpl implements SessionImplementor { + + /** + * Returns the underlying delegate. Be careful that it has a different behavior from the {@link #getDelegate()} + * method coming from the EntityManager interface which returns the current session. + * + * @see SessionLazyDelegatorBaseImpl#getDelegate() + */ + protected abstract SessionImplementor delegate(); + + @Override + public T execute(Callback callback) { + return delegate().execute( callback ); + } + + @Override + public String getTenantIdentifier() { + return delegate().getTenantIdentifier(); + } + + @Override + public UUID getSessionIdentifier() { + return delegate().getSessionIdentifier(); + } + + @Override + public JdbcConnectionAccess getJdbcConnectionAccess() { + return delegate().getJdbcConnectionAccess(); + } + + @Override + public EntityKey generateEntityKey(Object id, EntityPersister persister) { + return delegate().generateEntityKey( id, persister ); + } + + @Override + public Interceptor getInterceptor() { + return delegate().getInterceptor(); + } + + @Override + public void setAutoClear(boolean enabled) { + delegate().setAutoClear( enabled ); + } + + @Override + public boolean isTransactionInProgress() { + return delegate().isTransactionInProgress(); + } + + @Override + public void checkTransactionNeededForUpdateOperation(String exceptionMessage) { + delegate().checkTransactionNeededForUpdateOperation( exceptionMessage ); + } + + @Override + public void initializeCollection(PersistentCollection collection, boolean writing) throws HibernateException { + delegate().initializeCollection( collection, writing ); + } + + @Override + public Object internalLoad(String entityName, Object id, boolean eager, boolean nullable) throws HibernateException { + return delegate().internalLoad( entityName, id, eager, nullable ); + } + + @Override + public Object immediateLoad(String entityName, Object id) throws HibernateException { + return delegate().immediateLoad( entityName, id ); + } + + @Override + public long getTimestamp() { + return delegate().getTimestamp(); + } + + @Override + public SessionFactoryImplementor getFactory() { + return delegate().getFactory(); + } + + @Override + public EntityPersister getEntityPersister(String entityName, Object object) throws HibernateException { + return delegate().getEntityPersister( entityName, object ); + } + + @Override + public Object getEntityUsingInterceptor(EntityKey key) throws HibernateException { + return delegate().getEntityUsingInterceptor( key ); + } + + @Override + public Object getContextEntityIdentifier(Object object) { + return delegate().getContextEntityIdentifier( object ); + } + + @Override + public String bestGuessEntityName(Object object) { + return delegate().bestGuessEntityName( object ); + } + + @Override + public String guessEntityName(Object entity) throws HibernateException { + return delegate().guessEntityName( entity ); + } + + @Override + public Object instantiate(String entityName, Serializable id) throws HibernateException { + return delegate().instantiate( entityName, id ); + } + + @Override + public List list(NativeSQLQuerySpecification spec, QueryParameters queryParameters) throws HibernateException { + return delegate().list( spec, queryParameters ); + } + + @Override + public ScrollableResultsImplementor scroll(NativeSQLQuerySpecification spec, QueryParameters queryParameters) throws HibernateException { + return delegate().scroll( spec, queryParameters ); + } + + @Override + public int getDontFlushFromFind() { + return delegate().getDontFlushFromFind(); + } + + @Override + public PersistenceContext getPersistenceContext() { + return delegate().getPersistenceContext(); + } + + @Override + public CacheMode getCacheMode() { + return delegate().getCacheMode(); + } + + @Override + public void setCacheMode(CacheMode cm) { + delegate().setCacheMode( cm ); + } + + @Override + public boolean isOpen() { + return delegate().isOpen(); + } + + @Override + public boolean isConnected() { + return delegate().isConnected(); + } + + @Override + public void checkOpen(boolean markForRollbackIfClosed) { + delegate().checkOpen( markForRollbackIfClosed ); + } + + @Override + public void markForRollbackOnly() { + delegate().markForRollbackOnly(); + } + + @Override + public long getTransactionStartTimestamp() { + return delegate().getTransactionStartTimestamp(); + } + + @Override + public FlushModeType getFlushMode() { + return delegate().getFlushMode(); + } + + @Override + public void setFlushMode(FlushModeType flushModeType) { + delegate().setFlushMode( flushModeType ); + } + + @Override + public void setHibernateFlushMode(FlushMode flushMode) { + delegate().setHibernateFlushMode( flushMode ); + } + + @Override + public FlushMode getHibernateFlushMode() { + return delegate().getHibernateFlushMode(); + } + + @Override + public void setFlushMode(FlushMode fm) { + delegate().setHibernateFlushMode( fm ); + } + + @Override + public void lock(Object entity, LockModeType lockMode) { + delegate().lock( entity, lockMode ); + } + + @Override + public void lock(Object entity, LockModeType lockMode, Map properties) { + delegate().lock( entity, lockMode, properties ); + } + + @Override + public Connection connection() { + return delegate().connection(); + } + + @Override + public void flush() { + delegate().flush(); + } + + @Override + public boolean isEventSource() { + return delegate().isEventSource(); + } + + @Override + public void afterScrollOperation() { + delegate().afterScrollOperation(); + } + + @Override + public TransactionCoordinator getTransactionCoordinator() { + return delegate().getTransactionCoordinator(); + } + + @Override + public JdbcCoordinator getJdbcCoordinator() { + return delegate().getJdbcCoordinator(); + } + + @Override + public JdbcServices getJdbcServices() { + return delegate().getJdbcServices(); + } + + @Override + public JdbcSessionContext getJdbcSessionContext() { + return delegate().getJdbcSessionContext(); + } + + @Override + public boolean isClosed() { + return delegate().isClosed(); + } + + @Override + public void checkOpen() { + delegate().checkOpen(); + } + + @Override + public boolean isOpenOrWaitingForAutoClose() { + return delegate().isOpenOrWaitingForAutoClose(); + } + + @Override + public boolean shouldAutoClose() { + return delegate().shouldAutoClose(); + } + + @Override + public boolean isAutoCloseSessionEnabled() { + return delegate().isAutoCloseSessionEnabled(); + } + + @Override + public boolean isQueryParametersValidationEnabled() { + return delegate().isQueryParametersValidationEnabled(); + } + + @Override + public boolean shouldAutoJoinTransaction() { + return delegate().shouldAutoJoinTransaction(); + } + + @Override + public LoadQueryInfluencers getLoadQueryInfluencers() { + return delegate().getLoadQueryInfluencers(); + } + + @Override + public ExceptionConverter getExceptionConverter() { + return delegate().getExceptionConverter(); + } + + @Override + public PersistenceContext getPersistenceContextInternal() { + return delegate().getPersistenceContextInternal(); + } + + @Override + public boolean autoFlushIfRequired(Set querySpaces) throws HibernateException { + return delegate().autoFlushIfRequired( querySpaces ); + } + + @Override + public SessionEventListenerManager getEventListenerManager() { + return delegate().getEventListenerManager(); + } + + @Override + public Transaction accessTransaction() { + return delegate().accessTransaction(); + } + + @Override + public Transaction beginTransaction() { + return delegate().beginTransaction(); + } + + @Override + public Transaction getTransaction() { + return delegate().getTransaction(); + } + + @Override + public void startTransactionBoundary() { + delegate().startTransactionBoundary(); + } + + @Override + public CacheTransactionSynchronization getCacheTransactionSynchronization() { + return delegate().getCacheTransactionSynchronization(); + } + + @Override + public void afterTransactionBegin() { + delegate().afterTransactionBegin(); + } + + @Override + public void beforeTransactionCompletion() { + delegate().beforeTransactionCompletion(); + } + + @Override + public void afterTransactionCompletion(boolean successful, boolean delayed) { + delegate().afterTransactionCompletion( successful, delayed ); + } + + @Override + public void flushBeforeTransactionCompletion() { + delegate().flushBeforeTransactionCompletion(); + } + + @Override + public EntityManagerFactory getEntityManagerFactory() { + return delegate().getFactory(); + } + + @Override + public HibernateCriteriaBuilder getCriteriaBuilder() { + return delegate().getCriteriaBuilder(); + } + + @Override + public Metamodel getMetamodel() { + return delegate().getMetamodel(); + } + + @Override + public RootGraphImplementor createEntityGraph(Class rootType) { + return delegate().createEntityGraph( rootType ); + } + + @Override + public RootGraphImplementor createEntityGraph(String graphName) { + return delegate().createEntityGraph( graphName ); + } + + @Override + public RootGraphImplementor getEntityGraph(String graphName) { + return delegate().getEntityGraph( graphName ); + } + + @Override + public List> getEntityGraphs(Class entityClass) { + return delegate().getEntityGraphs( entityClass ); + } + + @Override + public QueryImplementor getNamedQuery(String name) { + return delegate().getNamedQuery( name ); + } + + @Override + public NativeQueryImplementor getNamedNativeQuery(String name) { + return delegate().getNamedNativeQuery( name ); + } + + @Override + public NativeQueryImplementor getNamedNativeQuery(String name, String resultSetMapping) { + return delegate().getNamedNativeQuery( name, resultSetMapping ); + } + + @Override + public QueryImplementor createQuery(String queryString) { + return delegate().createQuery( queryString ); + } + + @Override + public QueryImplementor createQuery(String queryString, Class resultType) { + return delegate().createQuery( queryString, resultType ); + } + + @Override + public QueryImplementor createQuery(CriteriaQuery criteriaQuery) { + return delegate().createQuery( criteriaQuery ); + } + + @Override + public QueryImplementor createQuery(CriteriaUpdate updateQuery) { + return delegate().createQuery( updateQuery ); + } + + @Override + public QueryImplementor createQuery(CriteriaDelete deleteQuery) { + return delegate().createQuery( deleteQuery ); + } + + @Override + public QueryImplementor createNamedQuery(String name) { + return delegate().createNamedQuery( name ); + } + + @Override + public QueryImplementor createNamedQuery(String name, Class resultClass) { + return delegate().createNamedQuery( name, resultClass ); + } + + @Override + public NativeQueryImplementor createNativeQuery(String sqlString) { + return delegate().createNativeQuery( sqlString ); + } + + @Override + public NativeQueryImplementor createNativeQuery(String sqlString, Class resultClass) { + return delegate().createNativeQuery( sqlString, resultClass ); + } + + @Override + public NativeQueryImplementor createNativeQuery(String sqlString, String resultSetMappingName) { + return delegate().createNativeQuery( sqlString, resultSetMappingName ); + } + + @Override + public StoredProcedureQuery createNamedStoredProcedureQuery(String name) { + return delegate().createNamedStoredProcedureQuery( name ); + } + + @Override + public StoredProcedureQuery createStoredProcedureQuery(String procedureName) { + return delegate().createStoredProcedureQuery( procedureName ); + } + + @Override + public StoredProcedureQuery createStoredProcedureQuery(String procedureName, Class... resultClasses) { + return delegate().createStoredProcedureQuery( procedureName, resultClasses ); + } + + @Override + public StoredProcedureQuery createStoredProcedureQuery(String procedureName, String... resultSetMappings) { + return delegate().createStoredProcedureQuery( procedureName, resultSetMappings ); + } + + @Override + public void prepareForQueryExecution(boolean requiresTxn) { + delegate().prepareForQueryExecution( requiresTxn ); + } + + @Override + public void joinTransaction() { + delegate().joinTransaction(); + } + + @Override + public boolean isJoinedToTransaction() { + return delegate().isJoinedToTransaction(); + } + + @Override + public T unwrap(Class cls) { + return delegate().unwrap( cls ); + } + + /** + * This is an implementation of EntityManager#getDelegate(). It returns the current session and not the delegate() + * session as it is what we want. The name of the method is misleading here but, as it is part of JPA, we cannot do + * anything about it. + *

+ * To get the underlying delegate(), use {@link #delegate()} instead. + * + * @see SessionLazyDelegatorBaseImpl#delegate() + */ + @Override + public Object getDelegate() { + return this; + } + + @Override + public ProcedureCall getNamedProcedureCall(String name) { + return delegate().getNamedProcedureCall( name ); + } + + @Override + public ProcedureCall createStoredProcedureCall(String procedureName) { + return delegate().createStoredProcedureCall( procedureName ); + } + + @Override + public ProcedureCall createStoredProcedureCall(String procedureName, Class... resultClasses) { + return delegate().createStoredProcedureCall( procedureName, resultClasses ); + } + + @Override + public ProcedureCall createStoredProcedureCall(String procedureName, String... resultSetMappings) { + return delegate().createStoredProcedureCall( procedureName, resultSetMappings ); + } + + @Override + public SharedSessionBuilder sessionWithOptions() { + return delegate().sessionWithOptions(); + } + + @Override + public SessionFactoryImplementor getSessionFactory() { + return delegate().getSessionFactory(); + } + + @Override + public void close() throws HibernateException { + delegate().close(); + } + + @Override + public void cancelQuery() throws HibernateException { + delegate().cancelQuery(); + } + + @Override + public boolean isDirty() throws HibernateException { + return delegate().isDirty(); + } + + @Override + public boolean isDefaultReadOnly() { + return delegate().isDefaultReadOnly(); + } + + @Override + public void setDefaultReadOnly(boolean readOnly) { + delegate().setDefaultReadOnly( readOnly ); + } + + @Override + public Object getIdentifier(Object object) { + return delegate().getIdentifier( object ); + } + + @Override + public boolean contains(String entityName, Object object) { + return delegate().contains( entityName, object ); + } + + @Override + public boolean contains(Object object) { + return delegate().contains( object ); + } + + @Override + public LockModeType getLockMode(Object entity) { + return delegate().getLockMode( entity ); + } + + @Override + public void setProperty(String propertyName, Object value) { + delegate().setProperty( propertyName, value ); + } + + @Override + public Map getProperties() { + return delegate().getProperties(); + } + + @Override + public void evict(Object object) { + delegate().evict( object ); + } + + @Override + public T load(Class theClass, Object id, LockMode lockMode) { + return delegate().load( theClass, id, lockMode ); + } + + @Override + public T load(Class theClass, Object id, LockOptions lockOptions) { + return delegate().load( theClass, id, lockOptions ); + } + + @Override + public Object load(String entityName, Object id, LockMode lockMode) { + return delegate().load( entityName, id, lockMode ); + } + + @Override + public Object load(String entityName, Object id, LockOptions lockOptions) { + return delegate().load( entityName, id, lockOptions ); + } + + @Override + public T load(Class theClass, Object id) { + return delegate().load( theClass, id ); + } + + @Override + public Object load(String entityName, Object id) { + return delegate().load( entityName, id ); + } + + @Override + public void load(Object object, Object id) { + delegate().load( object, id ); + } + + @Override + public void replicate(Object object, ReplicationMode replicationMode) { + delegate().replicate( object, replicationMode ); + } + + @Override + public void replicate(String entityName, Object object, ReplicationMode replicationMode) { + delegate().replicate( entityName, object, replicationMode ); + } + + @Override + public Object save(Object object) { + return delegate().save( object ); + } + + @Override + public Object save(String entityName, Object object) { + return delegate().save( entityName, object ); + } + + @Override + public void saveOrUpdate(Object object) { + delegate().saveOrUpdate( object ); + } + + @Override + public void saveOrUpdate(String entityName, Object object) { + delegate().saveOrUpdate( entityName, object ); + } + + @Override + public void update(Object object) { + delegate().update( object ); + } + + @Override + public void update(String entityName, Object object) { + delegate().update( entityName, object ); + } + + @Override + public Object merge(Object object) { + return delegate().merge( object ); + } + + @Override + public Object merge(String entityName, Object object) { + return delegate().merge( entityName, object ); + } + + @Override + public void persist(Object object) { + delegate().persist( object ); + } + + @Override + public void remove(Object entity) { + delegate().remove( entity ); + } + + @Override + public T find(Class entityClass, Object primaryKey) { + return delegate().find( entityClass, primaryKey ); + } + + @Override + public T find(Class entityClass, Object primaryKey, Map properties) { + return delegate().find( entityClass, primaryKey, properties ); + } + + @Override + public T find(Class entityClass, Object primaryKey, LockModeType lockMode) { + return delegate().find( entityClass, primaryKey, lockMode ); + } + + @Override + public T find(Class entityClass, Object primaryKey, LockModeType lockMode, Map properties) { + return delegate().find( entityClass, primaryKey, lockMode, properties ); + } + + @Override + public T getReference(Class entityClass, Object primaryKey) { + return delegate().getReference( entityClass, primaryKey ); + } + + @Override + public void persist(String entityName, Object object) { + delegate().persist( entityName, object ); + } + + @Override + public void delete(Object object) { + delegate().delete( object ); + } + + @Override + public void delete(String entityName, Object object) { + delegate().delete( entityName, object ); + } + + @Override + public void lock(Object object, LockMode lockMode) { + delegate().lock( object, lockMode ); + } + + @Override + public void lock(String entityName, Object object, LockMode lockMode) { + delegate().lock( entityName, object, lockMode ); + } + + @Override + public LockRequest buildLockRequest(LockOptions lockOptions) { + return delegate().buildLockRequest( lockOptions ); + } + + @Override + public void refresh(Object object) { + delegate().refresh( object ); + } + + @Override + public void refresh(Object entity, Map properties) { + delegate().refresh( entity, properties ); + } + + @Override + public void refresh(Object entity, LockModeType lockMode) { + delegate().refresh( entity, lockMode ); + } + + @Override + public void refresh(Object entity, LockModeType lockMode, Map properties) { + delegate().refresh( entity, lockMode, properties ); + } + + @Override + public void refresh(String entityName, Object object) { + delegate().refresh( entityName, object ); + } + + @Override + public void refresh(Object object, LockMode lockMode) { + delegate().refresh( object, lockMode ); + } + + @Override + public void refresh(Object object, LockOptions lockOptions) { + delegate().refresh( object, lockOptions ); + } + + @Override + public void refresh(String entityName, Object object, LockOptions lockOptions) { + delegate().refresh( entityName, object, lockOptions ); + } + + @Override + public LockMode getCurrentLockMode(Object object) { + return delegate().getCurrentLockMode( object ); + } + + @Override + public void clear() { + delegate().clear(); + } + + @Override + public void detach(Object entity) { + delegate().detach( entity ); + } + + @Override + public T get(Class theClass, Object id) { + return delegate().get( theClass, id ); + } + + @Override + public T get(Class theClass, Object id, LockMode lockMode) { + return delegate().get( theClass, id, lockMode ); + } + + @Override + public T get(Class theClass, Object id, LockOptions lockOptions) { + return delegate().get( theClass, id, lockOptions ); + } + + @Override + public Object get(String entityName, Object id) { + return delegate().get( entityName, id ); + } + + @Override + public Object get(String entityName, Serializable id, LockMode lockMode) { + return delegate().get( entityName, id, lockMode ); + } + + @Override + public Object get(String entityName, Object id, LockOptions lockOptions) { + return delegate().get( entityName, id, lockOptions ); + } + + @Override + public String getEntityName(Object object) { + return delegate().getEntityName( object ); + } + + @Override + public IdentifierLoadAccess byId(String entityName) { + return delegate().byId( entityName ); + } + + @Override + public MultiIdentifierLoadAccess byMultipleIds(Class entityClass) { + return delegate().byMultipleIds( entityClass ); + } + + @Override + public MultiIdentifierLoadAccess byMultipleIds(String entityName) { + return delegate().byMultipleIds( entityName ); + } + + @Override + public IdentifierLoadAccess byId(Class entityClass) { + return delegate().byId( entityClass ); + } + + @Override + public NaturalIdLoadAccess byNaturalId(String entityName) { + return delegate().byNaturalId( entityName ); + } + + @Override + public NaturalIdLoadAccess byNaturalId(Class entityClass) { + return delegate().byNaturalId( entityClass ); + } + + @Override + public SimpleNaturalIdLoadAccess bySimpleNaturalId(String entityName) { + return delegate().bySimpleNaturalId( entityName ); + } + + @Override + public SimpleNaturalIdLoadAccess bySimpleNaturalId(Class entityClass) { + return delegate().bySimpleNaturalId( entityClass ); + } + + @Override + public NaturalIdMultiLoadAccess byMultipleNaturalId(Class entityClass) { + return delegate().byMultipleNaturalId( entityClass ); + } + + @Override + public NaturalIdMultiLoadAccess byMultipleNaturalId(String entityName) { + return delegate().byMultipleNaturalId( entityName ); + } + + @Override + public Filter enableFilter(String filterName) { + return delegate().enableFilter( filterName ); + } + + @Override + public Filter getEnabledFilter(String filterName) { + return delegate().getEnabledFilter( filterName ); + } + + @Override + public void disableFilter(String filterName) { + delegate().disableFilter( filterName ); + } + + @Override + public SessionStatistics getStatistics() { + return delegate().getStatistics(); + } + + @Override + public boolean isReadOnly(Object entityOrProxy) { + return delegate().isReadOnly( entityOrProxy ); + } + + @Override + public void setReadOnly(Object entityOrProxy, boolean readOnly) { + delegate().setReadOnly( entityOrProxy, readOnly ); + } + + @Override + public void doWork(Work work) throws HibernateException { + delegate().doWork( work ); + } + + @Override + public T doReturningWork(ReturningWork work) throws HibernateException { + return delegate().doReturningWork( work ); + } + + @Override + public Connection disconnect() { + return delegate().disconnect(); + } + + @Override + public void reconnect(Connection connection) { + delegate().reconnect( connection ); + } + + @Override + public boolean isFetchProfileEnabled(String name) throws UnknownProfileException { + return delegate().isFetchProfileEnabled( name ); + } + + @Override + public void enableFetchProfile(String name) throws UnknownProfileException { + delegate().enableFetchProfile( name ); + } + + @Override + public void disableFetchProfile(String name) throws UnknownProfileException { + delegate().disableFetchProfile( name ); + } + + @Override + public LobHelper getLobHelper() { + return delegate().getLobHelper(); + } + + @Override + public void addEventListeners(SessionEventListener... listeners) { + delegate().addEventListeners( listeners ); + } + + @Override + public boolean isFlushBeforeCompletionEnabled() { + return delegate().isFlushBeforeCompletionEnabled(); + } + + @Override + public ActionQueue getActionQueue() { + return delegate().getActionQueue(); + } + + @Override + public Object instantiate(EntityPersister persister, Object id) throws HibernateException { + return delegate().instantiate( persister, id ); + } + + @Override + public void forceFlush(EntityEntry e) throws HibernateException { + delegate().forceFlush( e ); + } + + @Override + public void merge(String entityName, Object object, Map copiedAlready) throws HibernateException { + delegate().merge( entityName, object, copiedAlready ); + } + + @Override + public void persist(String entityName, Object object, Map createdAlready) throws HibernateException { + delegate().persist( entityName, object, createdAlready ); + } + + @Override + public void persistOnFlush(String entityName, Object object, Map copiedAlready) { + delegate().persistOnFlush( entityName, object, copiedAlready ); + } + + @Override + public void refresh(String entityName, Object object, Map refreshedAlready) throws HibernateException { + delegate().refresh( entityName, object, refreshedAlready ); + } + + @Override + public void delete(String entityName, Object child, boolean isCascadeDeleteEnabled, Set transientEntities) { + delegate().delete( entityName, child, isCascadeDeleteEnabled, transientEntities ); + } + + @Override + public void removeOrphanBeforeUpdates(String entityName, Object child) { + delegate().removeOrphanBeforeUpdates( entityName, child ); + } + + @Override + public SessionImplementor getSession() { + return this; + } + + @Override + public boolean useStreamForLobBinding() { + return delegate().useStreamForLobBinding(); + } + + @Override + public LobCreator getLobCreator() { + return delegate().getLobCreator(); + } + + @Override + public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) { + return delegate().remapSqlTypeDescriptor( sqlTypeDescriptor ); + } + + @Override + public Integer getJdbcBatchSize() { + return delegate().getJdbcBatchSize(); + } + + @Override + public void setJdbcBatchSize(Integer jdbcBatchSize) { + delegate().setJdbcBatchSize( jdbcBatchSize ); + } + + @Override + public TimeZone getJdbcTimeZone() { + return delegate().getJdbcTimeZone(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java index 6406d956d2..bcc9684066 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java @@ -200,7 +200,7 @@ public final class FastSessionServices { return elr.getEventListenerGroup( type ); } - SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) { + public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) { if ( !sqlTypeDescriptor.canBeRemapped() ) { return sqlTypeDescriptor; } @@ -288,6 +288,10 @@ public final class FastSessionServices { return defaultJdbcObservers; } + public boolean useStreamForLobBinding() { + return useStreamForLobBinding; + } + public void firePostLoadEvent(final PostLoadEvent postLoadEvent) { eventListenerGroup_POST_LOAD.fireEventOnEachListener( postLoadEvent, PostLoadEventListener::onPostLoad ); } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 465f3e74a0..bbe1ffedf5 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -221,7 +221,7 @@ public class SessionImpl //There might be custom properties for this session that affect the LockOptions state LockOptionsHelper.applyPropertiesToLockOptions( this.properties, this::getLockOptionsForWrite ); } - getSession().setCacheMode( fastSessionServices.initialSessionCacheMode ); + setCacheMode( fastSessionServices.initialSessionCacheMode ); // NOTE : pulse() already handles auto-join-ability correctly getTransactionCoordinator().pulse(); @@ -235,7 +235,7 @@ public class SessionImpl else { initialMode = ConfigurationHelper.getFlushMode( getSessionProperty( AvailableSettings.FLUSH_MODE ), FlushMode.AUTO ); } - getSession().setHibernateFlushMode( initialMode ); + setHibernateFlushMode( initialMode ); } if ( log.isTraceEnabled() ) { @@ -2872,7 +2872,7 @@ public class SessionImpl LockOptionsHelper.applyPropertiesToLockOptions( properties, this::getLockOptionsForWrite ); } else if ( JPA_SHARED_CACHE_RETRIEVE_MODE.equals( propertyName ) || JPA_SHARED_CACHE_STORE_MODE.equals( propertyName ) ) { - getSession().setCacheMode( + setCacheMode( CacheModeHelper.interpretCacheMode( determineCacheStoreMode( properties ), determineCacheRetrieveMode( properties ) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoadingBatcher.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoadingBatcher.java index 8b2a5b63a8..a060b23457 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoadingBatcher.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoadingBatcher.java @@ -37,7 +37,6 @@ import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.graph.entity.LoadingEntityEntry; import org.hibernate.sql.results.internal.RowTransformerPassThruImpl; -import org.hibernate.type.SerializableType; /** * Batch support for natural-id multi loading @@ -139,10 +138,17 @@ public class MultiNaturalIdLoadingBatcher { if ( needsExecution ) { while ( jdbcParamItr.hasNext() ) { + final JdbcParameterBindings jdbcParamBindingsRef = jdbcParamBindings; + final Iterator jdbcParamItrRef = jdbcParamItr; // pad the remaining parameters with null - jdbcParamBindings.addBinding( - jdbcParamItr.next(), - new JdbcParameterBindingImpl( SerializableType.INSTANCE, null ) + entityDescriptor.getNaturalIdMapping().visitJdbcValues( + null, + Clause.IRRELEVANT, + (jdbcValue, jdbcMapping) -> jdbcParamBindingsRef.addBinding( + jdbcParamItrRef.next(), + new JdbcParameterBindingImpl( jdbcMapping, jdbcValue ) + ), + session ); } final List batchResults = performLoad( jdbcParamBindings, session ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java index 3843e36601..80c886a88c 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java @@ -241,7 +241,7 @@ public abstract class AbstractCompositeIdentifierMapping } @Override - public Expression toSqlExpression( + public SqlTuple toSqlExpression( TableGroup tableGroup, Clause clause, SqmToSqlAstConverter walker, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableValuedModelPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableValuedModelPart.java index d6c7ed9482..8081a6e07a 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableValuedModelPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableValuedModelPart.java @@ -13,6 +13,7 @@ import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer; import org.hibernate.sql.results.graph.Fetchable; @@ -54,7 +55,7 @@ public interface EmbeddableValuedModelPart extends ModelPart, Fetchable, Fetchab return null; } - Expression toSqlExpression( + SqlTuple toSqlExpression( TableGroup tableGroup, Clause clause, SqmToSqlAstConverter walker, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/MappingModelHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/MappingModelHelper.java index f4edd0c7f1..bfda29445a 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/MappingModelHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/MappingModelHelper.java @@ -27,36 +27,7 @@ public class MappingModelHelper { SessionFactoryImplementor sessionFactory) { final int jdbcTypeCount = modelPart.getJdbcTypeCount( sessionFactory.getTypeConfiguration() ); - if ( jdbcTypeCount == 1 ) { - assert modelPart instanceof BasicValuedModelPart; - final BasicValuedModelPart basicPart = (BasicValuedModelPart) modelPart; - if ( sqlExpressionResolver == null ) { - return new ColumnReference( - basicPart.getContainingTableExpression(), - basicPart.getMappedColumnExpression(), - basicPart.isMappedColumnExpressionFormula(), - basicPart.getCustomReadExpression(), - basicPart.getCustomWriteExpression(), - basicPart.getJdbcMapping(), - sessionFactory - ); - } - else { - return sqlExpressionResolver.resolveSqlExpression( - createColumnReferenceKey( basicPart.getContainingTableExpression(), basicPart.getMappedColumnExpression() ), - sqlAstProcessingState -> new ColumnReference( - basicPart.getContainingTableExpression(), - basicPart.getMappedColumnExpression(), - basicPart.isMappedColumnExpressionFormula(), - basicPart.getCustomReadExpression(), - basicPart.getCustomWriteExpression(), - basicPart.getJdbcMapping(), - sessionFactory - ) - ); - } - } - else { + if ( modelPart instanceof EmbeddableValuedModelPart ) { final List columnReferences = new ArrayList<>( jdbcTypeCount ); modelPart.visitColumns( (table, column, isFormula, readFragment, writeFragment, jdbcMapping) -> { @@ -91,6 +62,35 @@ public class MappingModelHelper { ); return new SqlTuple( columnReferences, modelPart ); } + else { + assert modelPart instanceof BasicValuedModelPart; + final BasicValuedModelPart basicPart = (BasicValuedModelPart) modelPart; + if ( sqlExpressionResolver == null ) { + return new ColumnReference( + basicPart.getContainingTableExpression(), + basicPart.getMappedColumnExpression(), + basicPart.isMappedColumnExpressionFormula(), + basicPart.getCustomReadExpression(), + basicPart.getCustomWriteExpression(), + basicPart.getJdbcMapping(), + sessionFactory + ); + } + else { + return sqlExpressionResolver.resolveSqlExpression( + createColumnReferenceKey( basicPart.getContainingTableExpression(), basicPart.getMappedColumnExpression() ), + sqlAstProcessingState -> new ColumnReference( + basicPart.getContainingTableExpression(), + basicPart.getMappedColumnExpression(), + basicPart.isMappedColumnExpressionFormula(), + basicPart.getCustomReadExpression(), + basicPart.getCustomWriteExpression(), + basicPart.getJdbcMapping(), + sessionFactory + ) + ); + } + } } private MappingModelHelper() { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java index 9bbcb77058..92376603ac 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java @@ -223,7 +223,7 @@ public class EmbeddedAttributeMapping } @Override - public Expression toSqlExpression( + public SqlTuple toSqlExpression( TableGroup tableGroup, Clause clause, SqmToSqlAstConverter walker, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java index f731db19a5..524ffb0c18 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java @@ -210,7 +210,7 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF } @Override - public Expression toSqlExpression( + public SqlTuple toSqlExpression( TableGroup tableGroup, Clause clause, SqmToSqlAstConverter walker, diff --git a/hibernate-core/src/main/java/org/hibernate/query/CastType.java b/hibernate-core/src/main/java/org/hibernate/query/CastType.java index c3ef5505d0..1bb71ded49 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/CastType.java +++ b/hibernate-core/src/main/java/org/hibernate/query/CastType.java @@ -33,13 +33,23 @@ import java.time.*; * @author Gavin King */ public enum CastType { - STRING, - BOOLEAN, - INTEGER, LONG, FLOAT, DOUBLE, FIXED, - DATE, TIME, TIMESTAMP, - OFFSET_TIMESTAMP, ZONE_TIMESTAMP, - NULL, - OTHER; + 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); + + private final CastTypeKind kind; + + CastType(CastTypeKind kind) { + this.kind = kind; + } + + public CastTypeKind getKind() { + return kind; + } public static CastType from(Class javaClass) { if (String.class.equals(javaClass)) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/CastTypeKind.java b/hibernate-core/src/main/java/org/hibernate/query/CastTypeKind.java new file mode 100644 index 0000000000..e0d86961f1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/CastTypeKind.java @@ -0,0 +1,21 @@ +/* + * 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 +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/ComparisonOperator.java b/hibernate-core/src/main/java/org/hibernate/query/ComparisonOperator.java index 1ff9e3d46c..fbe0421856 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/ComparisonOperator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/ComparisonOperator.java @@ -12,10 +12,16 @@ package org.hibernate.query; */ public enum ComparisonOperator { EQUAL { + @Override public ComparisonOperator negated() { return NOT_EQUAL; } + @Override + public ComparisonOperator invert() { + return EQUAL; + } + @Override public String sqlText() { return "="; @@ -23,10 +29,16 @@ public enum ComparisonOperator { }, NOT_EQUAL { + @Override public ComparisonOperator negated() { return EQUAL; } + @Override + public ComparisonOperator invert() { + return NOT_EQUAL; + } + @Override public String sqlText() { return "!="; @@ -34,10 +46,16 @@ public enum ComparisonOperator { }, LESS_THAN { + @Override public ComparisonOperator negated() { return GREATER_THAN_OR_EQUAL; } + @Override + public ComparisonOperator invert() { + return GREATER_THAN; + } + @Override public String sqlText() { return "<"; @@ -45,10 +63,16 @@ public enum ComparisonOperator { }, LESS_THAN_OR_EQUAL { + @Override public ComparisonOperator negated() { return GREATER_THAN; } + @Override + public ComparisonOperator invert() { + return GREATER_THAN_OR_EQUAL; + } + @Override public String sqlText() { return "<="; @@ -56,10 +80,16 @@ public enum ComparisonOperator { }, GREATER_THAN { + @Override public ComparisonOperator negated() { return LESS_THAN_OR_EQUAL; } + @Override + public ComparisonOperator invert() { + return LESS_THAN; + } + @Override public String sqlText() { return ">"; @@ -67,10 +97,16 @@ public enum ComparisonOperator { }, GREATER_THAN_OR_EQUAL { + @Override public ComparisonOperator negated() { return LESS_THAN; } + @Override + public ComparisonOperator invert() { + return LESS_THAN_OR_EQUAL; + } + @Override public String sqlText() { return ">="; @@ -78,5 +114,6 @@ public enum ComparisonOperator { }; public abstract ComparisonOperator negated(); + public abstract ComparisonOperator invert(); public abstract String sqlText(); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/HibernateCriteriaBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/HibernateCriteriaBuilder.java index 25c0c1489c..0e90a977a3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/criteria/HibernateCriteriaBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/HibernateCriteriaBuilder.java @@ -32,6 +32,7 @@ import javax.persistence.criteria.Subquery; import org.hibernate.NullPrecedence; import org.hibernate.SortOrder; import org.hibernate.metamodel.model.domain.DomainType; +import org.hibernate.query.sqm.tree.expression.SqmExpression; /** * Hibernate extensions to the JPA CriteriaBuilder. @@ -330,6 +331,8 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder { > JpaExpression> indexes(L list); + SqmExpression value(T value); + > JpaExpression> values(C collection); @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaQueryStructure.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaQueryStructure.java index ffca9f2fa7..41437862a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaQueryStructure.java +++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaQueryStructure.java @@ -94,5 +94,5 @@ public interface JpaQueryStructure extends JpaCriteriaNode { JpaExpression getOffset(); - JpaQueryStructure setOffset(JpaExpression offset); + JpaQueryStructure setOffset(JpaExpression offset); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QuerySplitter.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QuerySplitter.java index 1b0a47c1d9..56c6a58946 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QuerySplitter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QuerySplitter.java @@ -6,7 +6,9 @@ */ package org.hibernate.query.hql.internal; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.hibernate.NotYetImplementedFor6Exception; @@ -58,8 +60,6 @@ import org.hibernate.query.sqm.tree.predicate.SqmWhereClause; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiation; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiationArgument; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiationTarget; -import org.hibernate.query.sqm.tree.select.SqmGroupByClause; -import org.hibernate.query.sqm.tree.select.SqmHavingClause; import org.hibernate.query.sqm.tree.select.SqmOrderByClause; import org.hibernate.query.sqm.tree.select.SqmQuerySpec; import org.hibernate.query.sqm.tree.select.SqmSelectClause; @@ -188,7 +188,8 @@ public class QuerySplitter { sqmQuerySpec.setFromClause( visitFromClause( querySpec.getFromClause() ) ); sqmQuerySpec.setSelectClause( visitSelectClause( querySpec.getSelectClause() ) ); sqmQuerySpec.setWhereClause( visitWhereClause( querySpec.getWhereClause() ) ); - sqmQuerySpec.setGroupByClause( visitGroupByClause( querySpec.getGroupByClause() ) ); + sqmQuerySpec.setGroupByClauseExpressions( visitGroupByClause( querySpec.getGroupByClauseExpressions() ) ); + sqmQuerySpec.setHavingClausePredicate( visitHavingClause( querySpec.getHavingClausePredicate() ) ); sqmQuerySpec.setOrderByClause( visitOrderByClause( querySpec.getOrderByClause() ) ); if ( querySpec.getLimitExpression() != null ) { sqmQuerySpec.setLimitExpression( (SqmExpression) querySpec.getLimitExpression().accept( this ) ); @@ -218,33 +219,23 @@ public class QuerySplitter { } @Override - public SqmGroupByClause visitGroupByClause(SqmGroupByClause clause) { - if ( clause == null ) { - return null; + public List> visitGroupByClause(List> groupByClauseExpressions) { + if ( groupByClauseExpressions.isEmpty() ) { + return groupByClauseExpressions; } - final SqmGroupByClause result = new SqmGroupByClause(); - clause.visitGroupings( - grouping -> result.addGrouping( - (SqmExpression) grouping.getExpression().accept( this ), - grouping.getCollation() - ) - ); - return result; + List> expressions = new ArrayList<>( groupByClauseExpressions.size() ); + for ( SqmExpression groupByClauseExpression : groupByClauseExpressions ) { + expressions.add( (SqmExpression) groupByClauseExpression.accept( this ) ); + } + return expressions; } @Override - public SqmGroupByClause.SqmGrouping visitGrouping(SqmGroupByClause.SqmGrouping grouping) { - throw new UnsupportedOperationException(); - } - - @Override - public SqmHavingClause visitHavingClause(SqmHavingClause clause) { - if ( clause == null || clause.getPredicate() == null ) { + public SqmPredicate visitHavingClause(SqmPredicate sqmPredicate) { + if ( sqmPredicate == null ) { return null; } - return new SqmHavingClause( - (SqmPredicate) clause.getPredicate().accept( this ) - ); + return (SqmPredicate) sqmPredicate.accept( this ); } @Override @@ -598,7 +589,6 @@ public class QuerySplitter { public SqmSortSpecification visitSortSpecification(SqmSortSpecification sortSpecification) { return new SqmSortSpecification( (SqmExpression) sortSpecification.getSortExpression().accept( this ), - sortSpecification.getCollation(), sortSpecification.getSortOrder(), sortSpecification.getNullPrecedence() ); @@ -638,7 +628,7 @@ public class QuerySplitter { } @Override - public SqmLiteral visitLiteral(SqmLiteral literal) { + public SqmLiteral visitLiteral(SqmLiteral literal) { return new SqmLiteral( literal.getLiteralValue(), literal.getNodeType(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java index 3e5a3388e6..8e7765cabd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java @@ -36,6 +36,7 @@ import org.hibernate.QueryException; import org.hibernate.SortOrder; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; +import org.hibernate.grammars.hql.HqlLexer; import org.hibernate.grammars.hql.HqlParser; import org.hibernate.grammars.hql.HqlParserBaseVisitor; import org.hibernate.internal.util.collections.Stack; @@ -101,6 +102,7 @@ import org.hibernate.query.sqm.tree.expression.SqmByUnit; import org.hibernate.query.sqm.tree.expression.SqmCaseSearched; import org.hibernate.query.sqm.tree.expression.SqmCaseSimple; import org.hibernate.query.sqm.tree.expression.SqmCastTarget; +import org.hibernate.query.sqm.tree.expression.SqmCollate; import org.hibernate.query.sqm.tree.expression.SqmCollectionSize; import org.hibernate.query.sqm.tree.expression.SqmDistinct; import org.hibernate.query.sqm.tree.expression.SqmDurationUnit; @@ -470,8 +472,8 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre // Query spec @Override - public SqmQuerySpec visitQuerySpec(HqlParser.QuerySpecContext ctx) { - final SqmQuerySpec sqmQuerySpec = new SqmQuerySpec( creationContext.getNodeBuilder() ); + public SqmQuerySpec visitQuerySpec(HqlParser.QuerySpecContext ctx) { + final SqmQuerySpec sqmQuerySpec = new SqmQuerySpec<>( creationContext.getNodeBuilder() ); // visit from-clause first!!! treatHandlerStack.push( new TreatHandlerFromClause() ); @@ -504,15 +506,25 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } sqmQuerySpec.setWhereClause( whereClause ); + final HqlParser.GroupByClauseContext groupByClauseContext = ctx.groupByClause(); + if ( groupByClauseContext != null ) { + sqmQuerySpec.setGroupByClauseExpressions( visitGroupByClause( groupByClauseContext ) ); + } + final HqlParser.HavingClauseContext havingClauseContext = ctx.havingClause(); + if ( havingClauseContext != null ) { + sqmQuerySpec.setHavingClausePredicate( visitHavingClause( havingClauseContext ) ); + } + final SqmOrderByClause orderByClause; - if ( ctx.orderByClause() != null ) { + final HqlParser.OrderByClauseContext orderByClauseContext = ctx.orderByClause(); + if ( orderByClauseContext != null ) { if ( creationOptions.useStrictJpaCompliance() && processingStateStack.depth() > 1 ) { throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.SUBQUERY_ORDER_BY ); } - orderByClause = visitOrderByClause( ctx.orderByClause() ); + orderByClause = visitOrderByClause( orderByClauseContext ); } else { orderByClause = new SqmOrderByClause(); @@ -743,13 +755,18 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public Object visitGroupByClause(HqlParser.GroupByClauseContext ctx) { - return super.visitGroupByClause( ctx ); + public List> visitGroupByClause(HqlParser.GroupByClauseContext ctx) { + final List expressionContexts = ctx.expression(); + final List> expressions = new ArrayList<>( expressionContexts.size() ); + for ( HqlParser.ExpressionContext expressionContext : expressionContexts ) { + expressions.add( (SqmExpression) expressionContext.accept( this ) ); + } + return expressions; } @Override - public Object visitHavingClause(HqlParser.HavingClauseContext ctx) { - return super.visitHavingClause( ctx ); + public SqmPredicate visitHavingClause(HqlParser.HavingClauseContext ctx) { + return (SqmPredicate) ctx.predicate().accept( this ); } @Override @@ -763,7 +780,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre @Override public SqmSortSpecification visitSortSpecification(HqlParser.SortSpecificationContext ctx) { - final SqmExpression sortExpression = visitSortExpression( ctx.sortExpression() ); + final SqmExpression sortExpression = visitSortExpression( ctx.sortExpression() ); if ( sortExpression == null ) { throw new ParsingException( "Could not resolve sort-expression : " + ctx.sortExpression().getText() ); } @@ -772,14 +789,6 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre HqlLogging.QUERY_LOGGER.debugf( "Questionable sorting by constant value : %s", sortExpression ); } - final String collation; - if ( ctx.collationSpecification() != null && ctx.collationSpecification().collateName() != null ) { - collation = ctx.collationSpecification().collateName().dotIdentifierSequence().getText(); - } - else { - collation = null; - } - final SortOrder sortOrder; if ( ctx.orderingSpecification() != null ) { final String ordering = ctx.orderingSpecification().getText(); @@ -802,76 +811,107 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre nullPrecedence = null; } - return new SqmSortSpecification( sortExpression, collation, sortOrder, nullPrecedence ); + return new SqmSortSpecification( sortExpression, sortOrder, nullPrecedence ); + } + + private SqmExpression wrapCollate(SqmExpression expression, HqlParser.CollationSpecificationContext collationSpecificationContext) { + if ( collationSpecificationContext == null ) { + return expression; + } + return new SqmCollate<>( expression, collationSpecificationContext.collateName().getText() ); } @Override - public SqmExpression visitSortExpression(HqlParser.SortExpressionContext ctx) { + public SqmExpression visitSortExpression(HqlParser.SortExpressionContext ctx) { if ( ctx.INTEGER_LITERAL() != null ) { + final HqlParser.CollationSpecificationContext collationSpecificationContext = ctx.collationSpecification(); final int position = Integer.parseInt( ctx.INTEGER_LITERAL().getText() ); final SqmSelection selection = getCurrentProcessingState().getPathRegistry().findSelectionByPosition( position ); if ( selection != null ) { final SqmSelectableNode selectableNode = selection.getSelectableNode(); if ( selectableNode instanceof SqmExpression ) { - return (SqmExpression) selectableNode; + return wrapCollate( (SqmExpression) selectableNode, collationSpecificationContext ); } } - return new SqmLiteral<>( position, resolveExpressableTypeBasic( Integer.class ), creationContext.getNodeBuilder() ); + return wrapCollate( + new SqmLiteral<>( + position, + resolveExpressableTypeBasic( Integer.class ), + creationContext.getNodeBuilder() + ), + collationSpecificationContext + ); } if ( ctx.identifier() != null ) { + final HqlParser.CollationSpecificationContext collationSpecificationContext = ctx.collationSpecification(); final SqmSelection selection = getCurrentProcessingState().getPathRegistry().findSelectionByAlias( ctx.identifier().getText() ); if ( selection != null ) { final SqmSelectableNode selectableNode = selection.getSelectableNode(); if ( selectableNode instanceof SqmExpression ) { - return (SqmExpression) selectableNode; + return wrapCollate( (SqmExpression) selectableNode, collationSpecificationContext ); } } final SqmFrom sqmFrom = getCurrentProcessingState().getPathRegistry().findFromByAlias( ctx.identifier().getText() ); if ( sqmFrom != null ) { + assert collationSpecificationContext == null; return sqmFrom; } final DotIdentifierConsumer dotIdentifierConsumer = dotIdentifierConsumerStack.getCurrent(); dotIdentifierConsumer.consumeIdentifier( ctx.getText(), true, true ); - return (SqmExpression) dotIdentifierConsumer.getConsumedPart(); + return wrapCollate( + (SqmExpression) dotIdentifierConsumer.getConsumedPart(), + collationSpecificationContext + ); } - return (SqmExpression) ctx.expression().accept( this ); + return (SqmExpression) ctx.expression().accept( this ); } @Override - public SqmExpression visitLimitClause(HqlParser.LimitClauseContext ctx) { + public SqmExpression visitLimitClause(HqlParser.LimitClauseContext ctx) { if ( ctx == null ) { return null; } - return (SqmExpression) ctx.parameterOrNumberLiteral().accept( this ); + return (SqmExpression) ctx.parameterOrNumberLiteral().accept( this ); } @Override - public SqmExpression visitOffsetClause(HqlParser.OffsetClauseContext ctx) { + public SqmExpression visitOffsetClause(HqlParser.OffsetClauseContext ctx) { if ( ctx == null ) { return null; } - return (SqmExpression) ctx.parameterOrNumberLiteral().accept( this ); + return (SqmExpression) ctx.parameterOrNumberLiteral().accept( this ); } @Override - public Object visitPathExpression(HqlParser.PathExpressionContext ctx) { - return ctx.path().accept( this ); + public SqmExpression visitPathExpression(HqlParser.PathExpressionContext ctx) { + return wrapCollate( + (SqmExpression) ctx.path().accept( this ), + ctx.collationSpecification() + ); } @Override - public SqmExpression visitParameterOrNumberLiteral(HqlParser.ParameterOrNumberLiteralContext ctx) { + public SqmExpression visitFunctionExpression(HqlParser.FunctionExpressionContext ctx) { + return wrapCollate( + (SqmExpression) ctx.function().accept( this ), + ctx.collationSpecification() + ); + } + + @Override + public SqmExpression visitParameterOrNumberLiteral(HqlParser.ParameterOrNumberLiteralContext ctx) { if ( ctx.INTEGER_LITERAL() != null ) { return integerLiteral( ctx.INTEGER_LITERAL().getText() ); } if ( ctx.parameter() != null ) { - return (SqmExpression) ctx.parameter().accept( this ); + return (SqmExpression) ctx.parameter().accept( this ); } return null; @@ -1501,8 +1541,11 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmPath visitEntityIdExpression(HqlParser.EntityIdExpressionContext ctx) { - return visitEntityIdReference( ctx.entityIdReference() ); + public SqmExpression visitEntityIdExpression(HqlParser.EntityIdExpressionContext ctx) { + return wrapCollate( + visitEntityIdReference( ctx.entityIdReference() ), + ctx.collationSpecification() + ); } @Override @@ -1528,8 +1571,11 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmPath visitEntityVersionExpression(HqlParser.EntityVersionExpressionContext ctx) { - return visitEntityVersionReference( ctx.entityVersionReference() ); + public SqmExpression visitEntityVersionExpression(HqlParser.EntityVersionExpressionContext ctx) { + return wrapCollate( + visitEntityVersionReference( ctx.entityVersionReference() ), + ctx.collationSpecification() + ); } @Override @@ -1716,6 +1762,16 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre return ctx.expression().accept(this); } + @Override + public Object visitCaseExpression(HqlParser.CaseExpressionContext ctx) { + final SqmExpression expression = (SqmExpression) ctx.caseList().accept( this ); + final HqlParser.CollationSpecificationContext collationSpecificationContext = ctx.collationSpecification(); + if ( collationSpecificationContext == null ) { + return expression; + } + return new SqmCollate<>( expression, collationSpecificationContext.collateName().getText() ); + } + @Override public SqmCaseSimple visitSimpleCaseList(HqlParser.SimpleCaseListContext ctx) { //noinspection unchecked @@ -1933,145 +1989,180 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmLiteral visitLiteralExpression(HqlParser.LiteralExpressionContext ctx) { - if ( ctx.literal().STRING_LITERAL() != null ) { - return stringLiteral( ctx.literal().STRING_LITERAL().getText() ); - } - else if ( ctx.literal().INTEGER_LITERAL() != null ) { - return integerLiteral( ctx.literal().INTEGER_LITERAL().getText() ); - } - else if ( ctx.literal().LONG_LITERAL() != null ) { - return longLiteral( ctx.literal().LONG_LITERAL().getText() ); - } - else if ( ctx.literal().BIG_INTEGER_LITERAL() != null ) { - return bigIntegerLiteral( ctx.literal().BIG_INTEGER_LITERAL().getText() ); - } - else if ( ctx.literal().HEX_LITERAL() != null ) { - return hexLiteral( ctx.literal().HEX_LITERAL().getText() ); - } - else if ( ctx.literal().FLOAT_LITERAL() != null ) { - return floatLiteral( ctx.literal().FLOAT_LITERAL().getText() ); - } - else if ( ctx.literal().DOUBLE_LITERAL() != null ) { - return doubleLiteral( ctx.literal().DOUBLE_LITERAL().getText() ); - } - else if ( ctx.literal().BIG_DECIMAL_LITERAL() != null ) { - return bigDecimalLiteral( ctx.literal().BIG_DECIMAL_LITERAL().getText() ); - } - else if ( ctx.literal().FALSE() != null ) { - return booleanLiteral( false ); - } - else if ( ctx.literal().TRUE() != null ) { - return booleanLiteral( true ); - } - else if ( ctx.literal().NULL() != null ) { - return new SqmLiteralNull( creationContext.getQueryEngine().getCriteriaBuilder() ); - } + public SqmExpression visitLiteralExpression(HqlParser.LiteralExpressionContext ctx) { + return wrapCollate( + (SqmExpression) ctx.literal().accept( this ), + ctx.collationSpecification() + ); + } - if ( ctx.literal().binaryLiteral() != null ) { - if ( ctx.literal().binaryLiteral().BINARY_LITERAL() != null ) { - return binaryLiteral( ctx.literal().binaryLiteral().BINARY_LITERAL().getText() ); - } - else { - StringBuilder text = new StringBuilder("x'"); - for ( TerminalNode hex : ctx.literal().binaryLiteral().HEX_LITERAL() ) { - if ( hex.getText().length()!=4 ) { - throw new LiteralNumberFormatException( "not a byte: " + hex.getText() ); - } - text.append( hex.getText().substring(2) ); + @Override + public Object visitBinaryLiteral(HqlParser.BinaryLiteralContext ctx) { + final TerminalNode binaryLiteral = ctx.BINARY_LITERAL(); + if ( binaryLiteral != null ) { + return binaryLiteral( binaryLiteral.getText() ); + } + else { + final StringBuilder text = new StringBuilder( "x'" ); + for ( TerminalNode hex : ctx.HEX_LITERAL() ) { + final String hexText = hex.getText(); + if ( hexText.length() != 4 ) { + throw new LiteralNumberFormatException( "not a byte: " + hexText ); } - return binaryLiteral( text.append("'").toString() ); + text.append( hexText.substring( 2 ) ); } + return binaryLiteral( text.append( "'" ).toString() ); } - - if ( ctx.literal().temporalLiteral() != null ) { - return interpretTemporalLiteral( ctx.literal().temporalLiteral() ); - } - - if ( ctx.literal().generalizedLiteral() != null ) { - throw new NotYetImplementedFor6Exception( getClass() ); - } - - // otherwise we have a problem - throw new ParsingException("Unexpected literal expression type [" + ctx.getText() + "]"); } - private SqmLiteral interpretTemporalLiteral(HqlParser.TemporalLiteralContext temporalLiteral) { - if ( temporalLiteral.dateTimeLiteral() != null ) { - if ( temporalLiteral.dateTimeLiteral().dateTime().offset()==null ) { - return dateTimeLiteralFrom( - temporalLiteral.dateTimeLiteral().dateTime().date(), - temporalLiteral.dateTimeLiteral().dateTime().time(), - temporalLiteral.dateTimeLiteral().dateTime().zoneId() - ); - } - else { - return offsetDatetimeLiteralFrom( - temporalLiteral.dateTimeLiteral().dateTime().date(), - temporalLiteral.dateTimeLiteral().dateTime().time(), - temporalLiteral.dateTimeLiteral().dateTime().offset() - ); - } + @Override + public Object visitGeneralizedLiteral(HqlParser.GeneralizedLiteralContext ctx) { + throw new NotYetImplementedFor6Exception( getClass() ); + } + + @Override + public SqmExpression visitTerminal(TerminalNode node) { + if ( node.getSymbol().getType() == HqlLexer.EOF ) { + return null; } - else if ( temporalLiteral.dateLiteral() != null ) { - return dateLiteralFrom( temporalLiteral.dateLiteral().date() ); + switch ( node.getSymbol().getType() ) { + case HqlParser.STRING_LITERAL: + return stringLiteral( node.getText() ); + case HqlParser.INTEGER_LITERAL: + return integerLiteral( node.getText() ); + case HqlParser.LONG_LITERAL: + return longLiteral( node.getText() ); + case HqlParser.BIG_INTEGER_LITERAL: + return bigIntegerLiteral( node.getText() ); + case HqlParser.HEX_LITERAL: + return hexLiteral( node.getText() ); + case HqlParser.FLOAT_LITERAL: + return floatLiteral( node.getText() ); + case HqlParser.DOUBLE_LITERAL: + return doubleLiteral( node.getText() ); + case HqlParser.BIG_DECIMAL_LITERAL: + return bigDecimalLiteral( node.getText() ); + case HqlParser.FALSE: + return booleanLiteral( false ); + case HqlParser.TRUE: + return booleanLiteral( true ); + case HqlParser.NULL: + return new SqmLiteralNull<>( creationContext.getQueryEngine().getCriteriaBuilder() ); + case HqlParser.BINARY_LITERAL: + return binaryLiteral( node.getText() ); + default: + throw new ParsingException("Unexpected terminal node [" + node.getText() + "]"); } - else if ( temporalLiteral.timeLiteral() != null ) { - return timeLiteralFrom( temporalLiteral.timeLiteral().time() ); - } - // literals for javax.sql Date/Time/Timestamp using JDBC escape syntax - else if ( temporalLiteral.jdbcTimestampLiteral() != null ) { - if ( temporalLiteral.jdbcTimestampLiteral().genericTemporalLiteralText()!=null ) { - return sqlTimestampLiteralFrom( temporalLiteral.jdbcTimestampLiteral().genericTemporalLiteralText().getText() ); - } - else { - return dateTimeLiteralFrom( - temporalLiteral.jdbcTimestampLiteral().dateTime().date(), - temporalLiteral.jdbcTimestampLiteral().dateTime().time(), - temporalLiteral.jdbcTimestampLiteral().dateTime().zoneId() - ); - } - } - else if ( temporalLiteral.jdbcDateLiteral() != null ) { - if ( temporalLiteral.jdbcDateLiteral().genericTemporalLiteralText()!=null ) { - return sqlDateLiteralFrom( temporalLiteral.jdbcDateLiteral().genericTemporalLiteralText().getText() ); - } - else { - return dateLiteralFrom( temporalLiteral.jdbcDateLiteral().date() ); - } - } - else if ( temporalLiteral.jdbcTimeLiteral() != null ) { - if ( temporalLiteral.jdbcTimeLiteral().genericTemporalLiteralText()!=null ) { - return sqlTimeLiteralFrom( temporalLiteral.jdbcTimeLiteral().genericTemporalLiteralText().getText() ); - } - else { - return timeLiteralFrom( temporalLiteral.jdbcTimeLiteral().time() ); - } + } + + @Override + public Object visitDateTimeLiteral(HqlParser.DateTimeLiteralContext dateTimeLiteralContext) { + final HqlParser.DateTimeContext dateTimeContext = dateTimeLiteralContext.dateTime(); + if ( dateTimeContext.offset() == null ) { + return dateTimeLiteralFrom( + dateTimeContext.date(), + dateTimeContext.time(), + dateTimeContext.zoneId() + ); } else { - // otherwise we have a problem - throw new ParsingException("Unexpected literal expression type [" + temporalLiteral.getText() + "]"); + return offsetDatetimeLiteralFrom( + dateTimeContext.date(), + dateTimeContext.time(), + dateTimeContext.offset() + ); } } - private SqmLiteral dateTimeLiteralFrom(HqlParser.DateContext date, HqlParser.TimeContext time, HqlParser.ZoneIdContext timezone) { - if (timezone == null) { + @Override + public Object visitDateLiteral(HqlParser.DateLiteralContext ctx) { + return ctx.date().accept( this ); + } + + @Override + public Object visitTimeLiteral(HqlParser.TimeLiteralContext ctx) { + return ctx.time().accept( this ); + } + + @Override + public Object visitJdbcTimestampLiteral(HqlParser.JdbcTimestampLiteralContext ctx) { + final HqlParser.GenericTemporalLiteralTextContext genericTemporalLiteralTextContext = ctx.genericTemporalLiteralText(); + if ( genericTemporalLiteralTextContext == null ) { + return ctx.dateTime().accept( this ); + } + else { + return sqlTimestampLiteralFrom( genericTemporalLiteralTextContext.getText() ); + } + } + + @Override + public Object visitJdbcDateLiteral(HqlParser.JdbcDateLiteralContext ctx) { + final HqlParser.GenericTemporalLiteralTextContext genericTemporalLiteralTextContext = ctx.genericTemporalLiteralText(); + if ( genericTemporalLiteralTextContext == null ) { + return ctx.date().accept( this ); + } + else { + return sqlDateLiteralFrom( genericTemporalLiteralTextContext.getText() ); + } + } + + @Override + public Object visitJdbcTimeLiteral(HqlParser.JdbcTimeLiteralContext ctx) { + final HqlParser.GenericTemporalLiteralTextContext genericTemporalLiteralTextContext = ctx.genericTemporalLiteralText(); + if ( genericTemporalLiteralTextContext == null ) { + return ctx.time().accept( this ); + } + else { + return sqlTimeLiteralFrom( genericTemporalLiteralTextContext.getText() ); + } + } + + @Override + public Object visitDateTime(HqlParser.DateTimeContext ctx) { + return dateTimeLiteralFrom( + ctx.date(), + ctx.time(), + ctx.zoneId() + ); + } + + private SqmLiteral dateTimeLiteralFrom( + HqlParser.DateContext date, + HqlParser.TimeContext time, + HqlParser.ZoneIdContext timezone) { + if ( timezone == null ) { return new SqmLiteral<>( - LocalDateTime.of(localDate(date), localTime(time)), - resolveExpressableTypeBasic(LocalDateTime.class), + LocalDateTime.of( localDate( date ), localTime( time ) ), + resolveExpressableTypeBasic( LocalDateTime.class ), creationContext.getNodeBuilder() ); } else { + final ZoneId zoneId = visitZoneId( timezone ); return new SqmLiteral<>( - ZonedDateTime.of(localDate(date), localTime(time), ZoneId.of(timezone.getText())), - resolveExpressableTypeBasic(ZonedDateTime.class), + ZonedDateTime.of( localDate( date ), localTime( time ), zoneId ), + resolveExpressableTypeBasic( ZonedDateTime.class ), creationContext.getNodeBuilder() ); } } - private SqmLiteral offsetDatetimeLiteralFrom(HqlParser.DateContext date, HqlParser.TimeContext time, HqlParser.OffsetContext offset) { + @Override + public ZoneId visitZoneId(HqlParser.ZoneIdContext ctx) { + final String timezoneText = ctx.getText(); + final String timezoneFullName = ZoneId.SHORT_IDS.get( timezoneText ); + if ( timezoneFullName == null ) { + return ZoneId.of( timezoneText ); + } + else { + return ZoneId.of( timezoneFullName ); + } + } + + private SqmLiteral offsetDatetimeLiteralFrom( + HqlParser.DateContext date, + HqlParser.TimeContext time, + HqlParser.OffsetContext offset) { return new SqmLiteral<>( OffsetDateTime.of( localDate( date ), localTime( time ), zoneOffset( offset ) ), resolveExpressableTypeBasic( OffsetDateTime.class ), @@ -2079,38 +2170,42 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre ); } - private SqmLiteral dateLiteralFrom(HqlParser.DateContext date) { + @Override + public Object visitDate(HqlParser.DateContext ctx) { return new SqmLiteral<>( - localDate( date ), + localDate( ctx ), resolveExpressableTypeBasic( LocalDate.class ), creationContext.getNodeBuilder() ); } - private SqmLiteral timeLiteralFrom(HqlParser.TimeContext time) { + @Override + public Object visitTime(HqlParser.TimeContext ctx) { return new SqmLiteral<>( - localTime( time ), + localTime( ctx ), resolveExpressableTypeBasic( LocalTime.class ), creationContext.getNodeBuilder() ); } private static LocalTime localTime(HqlParser.TimeContext ctx) { - if ( ctx.second() != null ) { - int index = ctx.second().getText().indexOf('.'); + final HqlParser.SecondContext second = ctx.second(); + if ( second != null ) { + final String secondText = second.getText(); + final int index = secondText.indexOf( '.'); if ( index < 0 ) { return LocalTime.of( Integer.parseInt( ctx.hour().getText() ), Integer.parseInt( ctx.minute().getText() ), - Integer.parseInt( ctx.second().getText() ) + Integer.parseInt( secondText ) ); } else { return LocalTime.of( Integer.parseInt( ctx.hour().getText() ), Integer.parseInt( ctx.minute().getText() ), - Integer.parseInt( ctx.second().getText().substring( 0, index ) ), - Integer.parseInt( ctx.second().getText().substring( index + 1 ) ) + Integer.parseInt( secondText.substring( 0, index ) ), + Integer.parseInt( secondText.substring( index + 1 ) ) ); } } @@ -2131,11 +2226,12 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } private static ZoneOffset zoneOffset(HqlParser.OffsetContext offset) { - return offset.minute() == null + final HqlParser.MinuteContext minute = offset.minute(); + return minute == null ? ZoneOffset.ofHours( Integer.parseInt( offset.hour().getText() ) ) : ZoneOffset.ofHoursMinutes( Integer.parseInt( offset.hour().getText() ), - Integer.parseInt( offset.minute().getText() ) + Integer.parseInt( minute.getText() ) ); } @@ -2242,7 +2338,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } private SqmLiteral binaryLiteral(String text) { - return new SqmLiteral( + return new SqmLiteral<>( StandardBasicTypes.BINARY.fromStringValue( text.substring( 2, text.length()-1 ) ), resolveExpressableTypeBasic( byte[].class ), creationContext.getNodeBuilder() @@ -2287,12 +2383,12 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } } - private SqmLiteral hexLiteral(String text) { + private SqmLiteral hexLiteral(String text) { final String originalText = text; text = text.substring( 2 ); try { final Number value; - final BasicDomainType type; + final BasicDomainType type; if ( text.endsWith( "l" ) || text.endsWith( "L" ) ) { text = text.substring( 0, text.length() - 1 ); value = Long.parseUnsignedLong( text, 16 ); @@ -2304,7 +2400,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } return new SqmLiteral<>( value, - type, + (SqmExpressable) type, creationContext.getNodeBuilder() ); } @@ -2395,13 +2491,12 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre @Override public Object visitParameterExpression(HqlParser.ParameterExpressionContext ctx) { - return ctx.parameter().accept( this ); + return wrapCollate( (SqmExpression) ctx.parameter().accept( this ), ctx.collationSpecification() ); } @Override - public SqmNamedParameter visitNamedParameter(HqlParser.NamedParameterContext ctx) { - - final SqmNamedParameter param = new SqmNamedParameter( + public SqmNamedParameter visitNamedParameter(HqlParser.NamedParameterContext ctx) { + final SqmNamedParameter param = new SqmNamedParameter<>( ctx.identifier().getText(), parameterDeclarationContextStack.getCurrent().isMultiValuedBindingAllowed(), creationContext.getNodeBuilder() @@ -2411,7 +2506,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmPositionalParameter visitPositionalParameter(HqlParser.PositionalParameterContext ctx) { + public SqmPositionalParameter visitPositionalParameter(HqlParser.PositionalParameterContext ctx) { if ( ctx.INTEGER_LITERAL() == null ) { throw new SemanticException( "Encountered positional parameter which did not declare position (? instead of, e.g., ?1)" ); } @@ -2429,7 +2524,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre // Functions @Override - public SqmExpression visitJpaNonStandardFunction(HqlParser.JpaNonStandardFunctionContext ctx) { + public SqmExpression visitJpaNonStandardFunction(HqlParser.JpaNonStandardFunctionContext ctx) { final String functionName = ctx.jpaNonStandardFunctionName().STRING_LITERAL().getText().toLowerCase(); List> functionArguments = ctx.nonStandardFunctionArguments() == null ? emptyList() : @@ -2449,7 +2544,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitNonStandardFunction(HqlParser.NonStandardFunctionContext ctx) { + public SqmExpression visitNonStandardFunction(HqlParser.NonStandardFunctionContext ctx) { if ( creationOptions.useStrictJpaCompliance() ) { throw new StrictJpaComplianceViolation( "Encountered non-compliant non-standard function call [" + @@ -2497,12 +2592,12 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre return arguments; } - private SqmExpression visitFinalFunctionArgument(HqlParser.ExpressionContext expression) { + private SqmExpression visitFinalFunctionArgument(HqlParser.ExpressionContext expression) { // the final argument to a function may accept multi-value parameter (varargs), // but only if we are operating in non-strict JPA mode parameterDeclarationContextStack.push( creationOptions::useStrictJpaCompliance ); try { - return (SqmExpression) expression.accept( this ); + return (SqmExpression) expression.accept( this ); } finally { parameterDeclarationContextStack.pop(); @@ -2510,8 +2605,8 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitCeilingFunction(HqlParser.CeilingFunctionContext ctx) { - final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); + public SqmExpression visitCeilingFunction(HqlParser.CeilingFunctionContext ctx) { + final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); return getFunctionDescriptor("ceiling").generateSqmExpression( arg, @@ -2522,8 +2617,8 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitFloorFunction(HqlParser.FloorFunctionContext ctx) { - final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); + public SqmExpression visitFloorFunction(HqlParser.FloorFunctionContext ctx) { + final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); return getFunctionDescriptor("floor").generateSqmExpression( arg, @@ -2538,20 +2633,20 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitAbsFunction(HqlParser.AbsFunctionContext ctx) { - final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); + public SqmExpression visitAbsFunction(HqlParser.AbsFunctionContext ctx) { + final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); return getFunctionDescriptor("abs").generateSqmExpression( arg, - (AllowableFunctionReturnType) arg.getNodeType(), + (AllowableFunctionReturnType) arg.getNodeType(), creationContext.getQueryEngine(), creationContext.getJpaMetamodel().getTypeConfiguration() ); } @Override - public SqmExpression visitSignFunction(HqlParser.SignFunctionContext ctx) { - final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); + public SqmExpression visitSignFunction(HqlParser.SignFunctionContext ctx) { + final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); return getFunctionDescriptor("sign").generateSqmExpression( arg, @@ -2562,34 +2657,34 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitModFunction(HqlParser.ModFunctionContext ctx) { - final SqmExpression dividend = (SqmExpression) ctx.modDividendArgument().accept( this ); - final SqmExpression divisor = (SqmExpression) ctx.modDivisorArgument().accept( this ); + public SqmExpression visitModFunction(HqlParser.ModFunctionContext ctx) { + final SqmExpression dividend = (SqmExpression) ctx.modDividendArgument().accept( this ); + final SqmExpression divisor = (SqmExpression) ctx.modDivisorArgument().accept( this ); return getFunctionDescriptor("mod").generateSqmExpression( asList( dividend, divisor ), - (AllowableFunctionReturnType) dividend.getNodeType(), + (AllowableFunctionReturnType) dividend.getNodeType(), creationContext.getQueryEngine(), creationContext.getJpaMetamodel().getTypeConfiguration() ); } @Override - public SqmExpression visitPowerFunction(HqlParser.PowerFunctionContext ctx) { - final SqmExpression base = (SqmExpression) ctx.powerBaseArgument().accept( this ); - final SqmExpression power = (SqmExpression) ctx.powerPowerArgument().accept( this ); + public SqmExpression visitPowerFunction(HqlParser.PowerFunctionContext ctx) { + final SqmExpression base = (SqmExpression) ctx.powerBaseArgument().accept( this ); + final SqmExpression power = (SqmExpression) ctx.powerPowerArgument().accept( this ); return getFunctionDescriptor("power").generateSqmExpression( asList( base, power ), - (AllowableFunctionReturnType) base.getNodeType(), + (AllowableFunctionReturnType) base.getNodeType(), creationContext.getQueryEngine(), creationContext.getJpaMetamodel().getTypeConfiguration() ); } @Override - public SqmExpression visitTrigFunction(HqlParser.TrigFunctionContext ctx) { - final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); + public SqmExpression visitTrigFunction(HqlParser.TrigFunctionContext ctx) { + final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); return getFunctionDescriptor( ctx.trigFunctionName().getText() ).generateSqmExpression( arg, @@ -2600,50 +2695,50 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitSqrtFunction(HqlParser.SqrtFunctionContext ctx) { - final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); + public SqmExpression visitSqrtFunction(HqlParser.SqrtFunctionContext ctx) { + final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); return getFunctionDescriptor("sqrt").generateSqmExpression( arg, - (AllowableFunctionReturnType) arg.getNodeType(), + (AllowableFunctionReturnType) arg.getNodeType(), creationContext.getQueryEngine(), creationContext.getJpaMetamodel().getTypeConfiguration() ); } @Override - public SqmExpression visitRoundFunction(HqlParser.RoundFunctionContext ctx) { - final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); - final SqmExpression precision = (SqmExpression) ctx.roundFunctionPrecision().expression().accept( this ); + public SqmExpression visitRoundFunction(HqlParser.RoundFunctionContext ctx) { + final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); + final SqmExpression precision = (SqmExpression) ctx.roundFunctionPrecision().expression().accept( this ); return getFunctionDescriptor("round").generateSqmExpression( asList(arg, precision), - (AllowableFunctionReturnType) arg.getNodeType(), + (AllowableFunctionReturnType) arg.getNodeType(), creationContext.getQueryEngine(), creationContext.getJpaMetamodel().getTypeConfiguration() ); } @Override - public SqmExpression visitAtan2Function(HqlParser.Atan2FunctionContext ctx) { - final SqmExpression sin = (SqmExpression) ctx.expression().get(0).accept( this ); - final SqmExpression cos = (SqmExpression) ctx.expression().get(1).accept( this ); + public SqmExpression visitAtan2Function(HqlParser.Atan2FunctionContext ctx) { + final SqmExpression sin = (SqmExpression) ctx.expression().get(0).accept( this ); + final SqmExpression cos = (SqmExpression) ctx.expression().get(1).accept( this ); return getFunctionDescriptor("atan2").generateSqmExpression( asList(sin, cos), - (AllowableFunctionReturnType) sin.getNodeType(), + (AllowableFunctionReturnType) sin.getNodeType(), creationContext.getQueryEngine(), creationContext.getJpaMetamodel().getTypeConfiguration() ); } @Override - public SqmExpression visitLnFunction(HqlParser.LnFunctionContext ctx) { - final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); + public SqmExpression visitLnFunction(HqlParser.LnFunctionContext ctx) { + final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); return getFunctionDescriptor("ln").generateSqmExpression( arg, - (AllowableFunctionReturnType) arg.getNodeType(), + (AllowableFunctionReturnType) arg.getNodeType(), creationContext.getQueryEngine(), creationContext.getJpaMetamodel().getTypeConfiguration() ); @@ -2651,12 +2746,12 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitExpFunction(HqlParser.ExpFunctionContext ctx) { - final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); + public SqmExpression visitExpFunction(HqlParser.ExpFunctionContext ctx) { + final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); return getFunctionDescriptor("exp").generateSqmExpression( arg, - (AllowableFunctionReturnType) arg.getNodeType(), + (AllowableFunctionReturnType) arg.getNodeType(), creationContext.getQueryEngine(), creationContext.getJpaMetamodel().getTypeConfiguration() ); @@ -2798,7 +2893,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre @Override public Object visitExtractFunction(HqlParser.ExtractFunctionContext ctx) { - final SqmExpression expressionToExtract = (SqmExpression) ctx.expression().accept( this ); + final SqmExpression expressionToExtract = (SqmExpression) ctx.expression().accept( this ); // visitDateOrTimeField() needs to know if we're extracting from a // JDBC Timestamp or from a java.time LocalDateTime/OffsetDateTime @@ -2861,9 +2956,9 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitFormatFunction(HqlParser.FormatFunctionContext ctx) { - final SqmExpression expressionToCast = (SqmExpression) ctx.expression().accept( this ); - final SqmLiteral format = (SqmLiteral) ctx.format().accept( this ); + public SqmExpression visitFormatFunction(HqlParser.FormatFunctionContext ctx) { + final SqmExpression expressionToCast = (SqmExpression) ctx.expression().accept( this ); + final SqmLiteral format = (SqmLiteral) ctx.format().accept( this ); return getFunctionDescriptor("format").generateSqmExpression( asList( expressionToCast, format ), @@ -2874,9 +2969,9 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitCastFunction(HqlParser.CastFunctionContext ctx) { - final SqmExpression expressionToCast = (SqmExpression) ctx.expression().accept( this ); - final SqmCastTarget castTargetExpression = (SqmCastTarget) ctx.castTarget().accept( this ); + public SqmExpression visitCastFunction(HqlParser.CastFunctionContext ctx) { + final SqmExpression expressionToCast = (SqmExpression) ctx.expression().accept( this ); + final SqmCastTarget castTargetExpression = (SqmCastTarget) ctx.castTarget().accept( this ); return getFunctionDescriptor("cast").generateSqmExpression( asList( expressionToCast, castTargetExpression ), @@ -2907,31 +3002,31 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitUpperFunction(HqlParser.UpperFunctionContext ctx) { - final SqmExpression expression = (SqmExpression) ctx.expression().accept( this ); + public SqmExpression visitUpperFunction(HqlParser.UpperFunctionContext ctx) { + final SqmExpression expression = (SqmExpression) ctx.expression().accept( this ); return getFunctionDescriptor("upper").generateSqmExpression( expression, - (AllowableFunctionReturnType) expression.getNodeType(), + (AllowableFunctionReturnType) expression.getNodeType(), creationContext.getQueryEngine(), creationContext.getJpaMetamodel().getTypeConfiguration() ); } @Override - public SqmExpression visitLowerFunction(HqlParser.LowerFunctionContext ctx) { + public SqmExpression visitLowerFunction(HqlParser.LowerFunctionContext ctx) { // todo (6.0) : why pass both the expression and its expression-type? // can't we just pass the expression? - final SqmExpression expression = (SqmExpression) ctx.expression().accept( this ); + final SqmExpression expression = (SqmExpression) ctx.expression().accept( this ); return getFunctionDescriptor("lower").generateSqmExpression( expression, - (AllowableFunctionReturnType) expression.getNodeType(), + (AllowableFunctionReturnType) expression.getNodeType(), creationContext.getQueryEngine(), creationContext.getJpaMetamodel().getTypeConfiguration() ); } @Override - public SqmExpression visitConcatFunction(HqlParser.ConcatFunctionContext ctx) { + public SqmExpression visitConcatFunction(HqlParser.ConcatFunctionContext ctx) { final List> arguments = new ArrayList<>(); for ( HqlParser.ExpressionContext argument : ctx.expression() ) { arguments.add( (SqmTypedNode) argument.accept( this ) ); @@ -2947,7 +3042,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre @Override public Object visitLengthFunction(HqlParser.LengthFunctionContext ctx) { - final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); + final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); return getFunctionDescriptor("length").generateSqmExpression( arg, @@ -2959,8 +3054,8 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre @Override public Object visitPositionFunction(HqlParser.PositionFunctionContext ctx) { - final SqmExpression string = (SqmExpression) ctx.positionFunctionStringArgument().accept( this ); - final SqmExpression pattern = (SqmExpression) ctx.positionFunctionPatternArgument().accept( this ); + final SqmExpression string = (SqmExpression) ctx.positionFunctionStringArgument().accept( this ); + final SqmExpression pattern = (SqmExpression) ctx.positionFunctionPatternArgument().accept( this ); return getFunctionDescriptor("position").generateSqmExpression( asList( pattern, string ), @@ -2972,8 +3067,8 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre @Override public Object visitLocateFunction(HqlParser.LocateFunctionContext ctx) { - final SqmExpression string = (SqmExpression) ctx.locateFunctionStringArgument().accept( this ); - final SqmExpression pattern = (SqmExpression) ctx.locateFunctionPatternArgument().accept( this ); + final SqmExpression string = (SqmExpression) ctx.locateFunctionStringArgument().accept( this ); + final SqmExpression pattern = (SqmExpression) ctx.locateFunctionPatternArgument().accept( this ); final SqmExpression start = ctx.locateFunctionStartArgument() == null ? null : (SqmExpression) ctx.locateFunctionStartArgument().accept( this ); @@ -2991,12 +3086,12 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre @Override public Object visitOverlayFunction(HqlParser.OverlayFunctionContext ctx) { - final SqmExpression string = (SqmExpression) ctx.overlayFunctionStringArgument().accept( this ); - final SqmExpression replacement = (SqmExpression) ctx.overlayFunctionReplacementArgument().accept( this ); - final SqmExpression start = (SqmExpression) ctx.overlayFunctionStartArgument().accept( this ); + final SqmExpression string = (SqmExpression) ctx.overlayFunctionStringArgument().accept( this ); + final SqmExpression replacement = (SqmExpression) ctx.overlayFunctionReplacementArgument().accept( this ); + final SqmExpression start = (SqmExpression) ctx.overlayFunctionStartArgument().accept( this ); final SqmExpression length = ctx.overlayFunctionLengthArgument() == null ? null - : (SqmExpression) ctx.overlayFunctionLengthArgument().accept( this ); + : (SqmExpression) ctx.overlayFunctionLengthArgument().accept( this ); return getFunctionDescriptor("overlay").generateSqmExpression( length == null @@ -3011,9 +3106,9 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre @Override public Object visitReplaceFunction(HqlParser.ReplaceFunctionContext ctx) { - final SqmExpression string = (SqmExpression) ctx.replaceFunctionStringArgument().accept( this ); - final SqmExpression pattern = (SqmExpression) ctx.replaceFunctionPatternArgument().accept( this ); - final SqmExpression replacement = (SqmExpression) ctx.replaceFunctionReplacementArgument().accept( this ); + final SqmExpression string = (SqmExpression) ctx.replaceFunctionStringArgument().accept( this ); + final SqmExpression pattern = (SqmExpression) ctx.replaceFunctionPatternArgument().accept( this ); + final SqmExpression replacement = (SqmExpression) ctx.replaceFunctionReplacementArgument().accept( this ); return getFunctionDescriptor("replace").generateSqmExpression( asList( string, pattern, replacement ), @@ -3025,7 +3120,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre @Override public Object visitStrFunction(HqlParser.StrFunctionContext ctx) { - final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); + final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); return getFunctionDescriptor("str").generateSqmExpression( singletonList( arg ), resolveExpressableTypeBasic( String.class ), @@ -3035,8 +3130,8 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitMaxFunction(HqlParser.MaxFunctionContext ctx) { - final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); + public SqmExpression visitMaxFunction(HqlParser.MaxFunctionContext ctx) { + final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); //ignore DISTINCT return getFunctionDescriptor("max").generateSqmExpression( arg, @@ -3047,8 +3142,8 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitMinFunction(HqlParser.MinFunctionContext ctx) { - final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); + public SqmExpression visitMinFunction(HqlParser.MinFunctionContext ctx) { + final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); //ignore DISTINCT return getFunctionDescriptor("min").generateSqmExpression( arg, @@ -3059,8 +3154,8 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitSumFunction(HqlParser.SumFunctionContext ctx) { - final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); + public SqmExpression visitSumFunction(HqlParser.SumFunctionContext ctx) { + final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); final SqmTypedNode argument = ctx.DISTINCT() != null ? new SqmDistinct<>(arg, getCreationContext().getNodeBuilder()) : arg; @@ -3074,14 +3169,14 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitEveryFunction(HqlParser.EveryFunctionContext ctx) { + public SqmExpression visitEveryFunction(HqlParser.EveryFunctionContext ctx) { if ( ctx.subQuery()!=null ) { SqmSubQuery subquery = (SqmSubQuery) ctx.subQuery().accept(this); - return new SqmEvery( subquery, subquery.getNodeType(), creationContext.getNodeBuilder() ); + return new SqmEvery<>( subquery, creationContext.getNodeBuilder() ); } - final SqmExpression argument = (SqmExpression) ctx.predicate().accept( this ); + final SqmExpression argument = (SqmExpression) ctx.predicate().accept( this ); return getFunctionDescriptor("every").generateSqmExpression( argument, @@ -3092,14 +3187,14 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitAnyFunction(HqlParser.AnyFunctionContext ctx) { + public SqmExpression visitAnyFunction(HqlParser.AnyFunctionContext ctx) { if ( ctx.subQuery()!=null ) { SqmSubQuery subquery = (SqmSubQuery) ctx.subQuery().accept(this); - return new SqmAny( subquery, subquery.getNodeType(), creationContext.getNodeBuilder() ); + return new SqmAny<>( subquery, creationContext.getNodeBuilder() ); } - final SqmExpression argument = (SqmExpression) ctx.predicate().accept( this ); + final SqmExpression argument = (SqmExpression) ctx.predicate().accept( this ); return getFunctionDescriptor("any").generateSqmExpression( argument, @@ -3110,8 +3205,8 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitAvgFunction(HqlParser.AvgFunctionContext ctx) { - final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); + public SqmExpression visitAvgFunction(HqlParser.AvgFunctionContext ctx) { + final SqmExpression arg = (SqmExpression) ctx.expression().accept( this ); final SqmTypedNode argument = ctx.DISTINCT() != null ? new SqmDistinct<>( arg, getCreationContext().getNodeBuilder() ) : arg; @@ -3125,10 +3220,10 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitCountFunction(HqlParser.CountFunctionContext ctx) { + public SqmExpression visitCountFunction(HqlParser.CountFunctionContext ctx) { final SqmExpression arg = ctx.ASTERISK() != null ? new SqmStar( getCreationContext().getNodeBuilder() ) - : (SqmExpression) ctx.expression().accept( this ); + : (SqmExpression) ctx.expression().accept( this ); final SqmTypedNode argument = ctx.DISTINCT() != null ? new SqmDistinct<>( arg, getCreationContext().getNodeBuilder() ) : arg; @@ -3142,10 +3237,10 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitCube(HqlParser.CubeContext ctx) { + public SqmExpression visitCube(HqlParser.CubeContext ctx) { List> args = new ArrayList<>(); for ( HqlParser.ExpressionContext arg: ctx.expression() ) { - args.add( (SqmExpression) arg.accept( this ) ); + args.add( (SqmExpression) arg.accept( this ) ); } //ignore DISTINCT return getFunctionDescriptor("cube").generateSqmExpression( @@ -3157,10 +3252,10 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitRollup(HqlParser.RollupContext ctx) { + public SqmExpression visitRollup(HqlParser.RollupContext ctx) { List> args = new ArrayList<>(); for ( HqlParser.ExpressionContext arg: ctx.expression() ) { - args.add( (SqmExpression) arg.accept( this ) ); + args.add( (SqmExpression) arg.accept( this ) ); } //ignore DISTINCT return getFunctionDescriptor("rollup").generateSqmExpression( @@ -3172,12 +3267,12 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitSubstringFunction(HqlParser.SubstringFunctionContext ctx) { - final SqmExpression source = (SqmExpression) ctx.expression().accept( this ); - final SqmExpression start = (SqmExpression) ctx.substringFunctionStartArgument().accept( this ); - final SqmExpression length = ctx.substringFunctionLengthArgument() == null + public SqmExpression visitSubstringFunction(HqlParser.SubstringFunctionContext ctx) { + final SqmExpression source = (SqmExpression) ctx.expression().accept( this ); + final SqmExpression start = (SqmExpression) ctx.substringFunctionStartArgument().accept( this ); + final SqmExpression length = ctx.substringFunctionLengthArgument() == null ? null - : (SqmExpression) ctx.substringFunctionLengthArgument().accept( this ); + : (SqmExpression) ctx.substringFunctionLengthArgument().accept( this ); return getFunctionDescriptor("substring").generateSqmExpression( length==null ? asList( source, start ) : asList( source, start, length ), @@ -3188,9 +3283,9 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitLeftFunction(HqlParser.LeftFunctionContext ctx) { - final SqmExpression source = (SqmExpression) ctx.expression(0).accept( this ); - final SqmExpression length = (SqmExpression) ctx.expression(1).accept( this ); + public SqmExpression visitLeftFunction(HqlParser.LeftFunctionContext ctx) { + final SqmExpression source = (SqmExpression) ctx.expression(0).accept( this ); + final SqmExpression length = (SqmExpression) ctx.expression(1).accept( this ); return getFunctionDescriptor("left").generateSqmExpression( asList( source, length ), @@ -3201,9 +3296,9 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitRightFunction(HqlParser.RightFunctionContext ctx) { - final SqmExpression source = (SqmExpression) ctx.expression(0).accept( this ); - final SqmExpression length = (SqmExpression) ctx.expression(1).accept( this ); + public SqmExpression visitRightFunction(HqlParser.RightFunctionContext ctx) { + final SqmExpression source = (SqmExpression) ctx.expression(0).accept( this ); + final SqmExpression length = (SqmExpression) ctx.expression(1).accept( this ); return getFunctionDescriptor("right").generateSqmExpression( asList( source, length ), @@ -3215,8 +3310,8 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre @Override public SqmExpression visitPadFunction(HqlParser.PadFunctionContext ctx) { - final SqmExpression source = (SqmExpression) ctx.expression().accept( this ); - SqmExpression length = (SqmExpression) ctx.padLength().accept(this); + final SqmExpression source = (SqmExpression) ctx.expression().accept( this ); + SqmExpression length = (SqmExpression) ctx.padLength().accept(this); SqmTrimSpecification padSpec = visitPadSpecification( ctx.padSpecification() ); SqmLiteral padChar = ctx.padCharacter() == null ? null @@ -3264,8 +3359,8 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmExpression visitTrimFunction(HqlParser.TrimFunctionContext ctx) { - final SqmExpression source = (SqmExpression) ctx.expression().accept( this ); + public SqmExpression visitTrimFunction(HqlParser.TrimFunctionContext ctx) { + final SqmExpression source = (SqmExpression) ctx.expression().accept( this ); SqmTrimSpecification trimSpec = visitTrimSpecification( ctx.trimSpecification() ); SqmLiteral trimChar = visitTrimCharacter( ctx.trimCharacter() ); @@ -3327,7 +3422,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmPath visitCollectionIndexFunction(HqlParser.CollectionIndexFunctionContext ctx) { + public SqmPath visitCollectionIndexFunction(HqlParser.CollectionIndexFunctionContext ctx) { final String alias = ctx.identifier().getText(); final SqmFrom sqmFrom = processingStateStack.getCurrent().getPathRegistry().findFromByAlias( alias ); @@ -3343,14 +3438,14 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } //noinspection unchecked - return ( (PluralPersistentAttribute) pluralAttribute ).getIndexPathSource().createSqmPath( + return ( (PluralPersistentAttribute) pluralAttribute ).getIndexPathSource().createSqmPath( sqmFrom, this ); } @SuppressWarnings("BooleanMethodIsAlwaysInverted") - private boolean isIndexedPluralAttribute(SqmPath path) { + private boolean isIndexedPluralAttribute(SqmPath path) { return path.getReferencedPathSource() instanceof PluralPersistentAttribute; } @@ -3413,12 +3508,12 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre } @Override - public SqmSubQuery visitSubQueryExpression(HqlParser.SubQueryExpressionContext ctx) { + public SqmSubQuery visitSubQueryExpression(HqlParser.SubQueryExpressionContext ctx) { return visitSubQuery( ctx.subQuery() ); } @Override - public SqmSubQuery visitSubQuery(HqlParser.SubQueryContext ctx) { + public SqmSubQuery visitSubQuery(HqlParser.SubQueryContext ctx) { if ( ctx.querySpec().selectClause() == null ) { throw new SemanticException( "Sub-query cannot use implicit select-clause : " + ctx.getText() ); } @@ -3438,7 +3533,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre try { //noinspection unchecked - subQuery.setQuerySpec( visitQuerySpec( ctx.querySpec() ) ); + subQuery.setQuerySpec( (SqmQuerySpec) visitQuerySpec( ctx.querySpec() ) ); return subQuery; } finally { diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java index 857e792ef3..f7db74ca3f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java @@ -6,23 +6,23 @@ */ package org.hibernate.query.internal; +import java.util.Arrays; import java.util.IdentityHashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; import org.hibernate.Incubating; -import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.QueryException; import org.hibernate.QueryParameterException; import org.hibernate.cache.spi.QueryKey; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.query.QueryParameter; import org.hibernate.query.spi.ParameterMetadataImplementor; import org.hibernate.query.spi.QueryParameterBinding; import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.QueryParameterImplementor; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; /** * Manages the group of QueryParameterBinding for a particular query. @@ -179,21 +179,50 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings { @Override public QueryKey.ParameterBindingsMemento generateQueryKeyMemento() { - // todo (6.0) : need to decide how to handle - if ( parameterMetadata.getParameterCount() == 0 && CollectionHelper.isEmpty( parameterBindingMap ) ) { - return new QueryKey.ParameterBindingsMemento() { - @Override - public int hashCode() { - return QueryParameterBindingsImpl.class.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return obj instanceof QueryKey.ParameterBindingsMemento; - } - }; + final int size = parameterBindingMap.size(); + final Object[] values = new Object[size]; + int i = 0; + int hashCode = 0; + for ( QueryParameterBinding binding : parameterBindingMap.values() ) { + JavaTypeDescriptor javaTypeDescriptor = binding.getBindType().getExpressableJavaTypeDescriptor(); + final Object value = javaTypeDescriptor.getMutabilityPlan().deepCopy( binding.getBindValue() ); + hashCode = 31 * hashCode + javaTypeDescriptor.extractHashCode( value ); + values[i] = value; } - throw new NotYetImplementedFor6Exception( getClass() ); + return new ParameterBindingsMementoImpl( values, hashCode); + } + + private static class ParameterBindingsMementoImpl implements QueryKey.ParameterBindingsMemento { + private final Object[] values; + private final int hashCode; + + private ParameterBindingsMementoImpl(Object[] values, int hashCode) { + this.values = values; + this.hashCode = hashCode; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + ParameterBindingsMementoImpl queryKey = (ParameterBindingsMementoImpl) o; + + if ( hashCode != queryKey.hashCode ) { + return false; + } + // Probably incorrect - comparing Object[] arrays with Arrays.equals + return Arrays.equals( values, queryKey.values ); + } + + @Override + public int hashCode() { + return hashCode; + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/SqlSelectionImpl.java b/hibernate-core/src/main/java/org/hibernate/query/results/SqlSelectionImpl.java index 6b47d862e1..9e238ff6ce 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/SqlSelectionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/SqlSelectionImpl.java @@ -60,6 +60,11 @@ public class SqlSelectionImpl implements SqlSelection, Expression, SqlExpression return valuesArrayPosition; } + @Override + public Expression getExpression() { + return this; + } + @Override public MappingModelExpressable getExpressionType() { return valueMapping; diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java index 410c7592d7..658b700c71 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java @@ -10,6 +10,7 @@ import org.hibernate.HibernateException; import org.hibernate.Incubating; import org.hibernate.boot.spi.BootstrapContext; import org.hibernate.boot.spi.MetadataImplementor; +import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.Dialect; import org.hibernate.engine.config.spi.ConfigurationService; @@ -19,6 +20,7 @@ import org.hibernate.internal.CoreLogging; import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.metamodel.model.domain.JpaMetamodel; import org.hibernate.query.QueryLogging; +import org.hibernate.query.criteria.LiteralHandlingMode; import org.hibernate.query.hql.HqlTranslator; import org.hibernate.query.hql.internal.StandardHqlTranslator; import org.hibernate.query.hql.spi.SqmCreationOptions; @@ -68,6 +70,7 @@ public class QueryEngine { return new QueryEngine( () -> sessionFactory.getRuntimeMetamodels().getJpaMetamodel(), + sessionFactory.getSessionFactoryOptions().getCriteriaLiteralHandlingMode(), metadata.buildNamedQueryRepository( sessionFactory ), hqlTranslator, sqmTranslatorFactory, @@ -89,6 +92,7 @@ public class QueryEngine { public QueryEngine( Supplier jpaMetamodelAccess, + LiteralHandlingMode criteriaLiteralHandlingMode, NamedObjectRepository namedObjectRepository, HqlTranslator hqlTranslator, SqmTranslatorFactory sqmTranslatorFactory, @@ -106,7 +110,8 @@ public class QueryEngine { this.criteriaBuilder = new SqmCriteriaNodeBuilder( this, jpaMetamodelAccess, - serviceRegistry + serviceRegistry, + criteriaLiteralHandlingMode ); this.sqmFunctionRegistry = new SqmFunctionRegistry(); @@ -132,6 +137,7 @@ public class QueryEngine { */ public QueryEngine( JpaMetamodel jpaMetamodel, + LiteralHandlingMode criteriaLiteralHandlingMode, boolean useStrictJpaCompliance, NamedObjectRepository namedObjectRepository, NativeQueryInterpreter nativeQueryInterpreter, @@ -147,7 +153,8 @@ public class QueryEngine { this.criteriaBuilder = new SqmCriteriaNodeBuilder( this, () -> jpaMetamodel, - serviceRegistry + serviceRegistry, + criteriaLiteralHandlingMode ); final SqmCreationContext sqmCreationContext = new SqmCreationContext() { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java index cc5f13fac3..600b1373a8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java @@ -6,6 +6,8 @@ */ package org.hibernate.query.sqm; +import java.util.List; + import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.query.sqm.tree.cte.SqmCteConsumer; import org.hibernate.query.sqm.tree.cte.SqmCteStatement; @@ -30,6 +32,7 @@ import org.hibernate.query.sqm.tree.expression.SqmCaseSearched; import org.hibernate.query.sqm.tree.expression.SqmCaseSimple; import org.hibernate.query.sqm.tree.expression.SqmCastTarget; import org.hibernate.query.sqm.tree.expression.SqmCoalesce; +import org.hibernate.query.sqm.tree.expression.SqmCollate; import org.hibernate.query.sqm.tree.expression.SqmCollectionSize; import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter; import org.hibernate.query.sqm.tree.expression.SqmDurationUnit; @@ -75,10 +78,9 @@ import org.hibernate.query.sqm.tree.predicate.SqmMemberOfPredicate; import org.hibernate.query.sqm.tree.predicate.SqmNegatedPredicate; import org.hibernate.query.sqm.tree.predicate.SqmNullnessPredicate; import org.hibernate.query.sqm.tree.predicate.SqmOrPredicate; +import org.hibernate.query.sqm.tree.predicate.SqmPredicate; import org.hibernate.query.sqm.tree.predicate.SqmWhereClause; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiation; -import org.hibernate.query.sqm.tree.select.SqmGroupByClause; -import org.hibernate.query.sqm.tree.select.SqmHavingClause; import org.hibernate.query.sqm.tree.select.SqmJpaCompoundSelection; import org.hibernate.query.sqm.tree.select.SqmOrderByClause; import org.hibernate.query.sqm.tree.select.SqmQuerySpec; @@ -165,11 +167,9 @@ public interface SemanticQueryWalker { T visitValues(SqmValues values); - T visitGroupByClause(SqmGroupByClause clause); + T visitGroupByClause(List> groupByClauseExpressions); - T visitGrouping(SqmGroupByClause.SqmGrouping grouping); - - T visitHavingClause(SqmHavingClause clause); + T visitHavingClause(SqmPredicate clause); T visitDynamicInstantiation(SqmDynamicInstantiation sqmDynamicInstantiation); @@ -189,6 +189,8 @@ public interface SemanticQueryWalker { T visitTuple(SqmTuple sqmTuple); + T visitCollate(SqmCollate sqmCollate); + T visitBinaryArithmeticExpression(SqmBinaryArithmetic expression); T visitSubQueryExpression(SqmSubQuery expression); @@ -200,6 +202,7 @@ public interface SemanticQueryWalker { T visitSearchedCaseExpression(SqmCaseSearched expression); T visitAny(SqmAny sqmAny); + T visitEvery(SqmEvery sqmEvery); T visitPositionalParameterExpression(SqmPositionalParameter expression); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java index 5b72d48610..8cb191654b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java @@ -52,6 +52,8 @@ import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.tree.SqmDmlStatement; import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; +import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter; +import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper; import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.from.SqmRoot; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; @@ -230,6 +232,17 @@ public class QuerySqmImpl } this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, producer.getFactory() ); + // Parameters might be created through HibernateCriteriaBuilder.value which we need to bind here + for ( SqmParameter sqmParameter : this.domainParameterXref.getParameterResolutions().getSqmParameters() ) { + if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper ) { + final JpaCriteriaParameter jpaCriteriaParameter = ( (SqmJpaCriteriaParameterWrapper) sqmParameter ).getJpaCriteriaParameter(); + final Object value = jpaCriteriaParameter.getValue(); + // We don't set a null value, unless the type is also null which is the case when using HibernateCriteriaBuilder.value + if ( value != null || jpaCriteriaParameter.getNodeType() == null ) { + setParameter( jpaCriteriaParameter, value ); + } + } + } } private static void checkQueryReturnType(SqmSelectStatement sqm, Class resultClass, SessionFactoryImplementor sessionFactory) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java index 6ca16a538c..b94aa52fd4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java @@ -36,6 +36,7 @@ import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NullPrecedence; import org.hibernate.QueryException; import org.hibernate.SortOrder; +import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType; @@ -52,6 +53,7 @@ import org.hibernate.query.criteria.JpaCoalesce; import org.hibernate.query.criteria.JpaCompoundSelection; import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.criteria.JpaSelection; +import org.hibernate.query.criteria.LiteralHandlingMode; import org.hibernate.query.internal.QueryHelper; import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.sqm.NodeBuilder; @@ -104,6 +106,7 @@ import org.hibernate.query.sqm.tree.select.SqmSortSpecification; import org.hibernate.query.sqm.tree.select.SqmSubQuery; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.service.ServiceRegistry; +import org.hibernate.type.BasicType; import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; @@ -124,21 +127,25 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { return new SqmCriteriaNodeBuilder( sf.getQueryEngine(), () -> sf.getRuntimeMetamodels().getJpaMetamodel(), - sf.getServiceRegistry() + sf.getServiceRegistry(), + sf.getSessionFactoryOptions().getCriteriaLiteralHandlingMode() ); } private final QueryEngine queryEngine; private final Supplier domainModelAccess; private final ServiceRegistry serviceRegistry; + private final LiteralHandlingMode criteriaLiteralHandlingMode; public SqmCriteriaNodeBuilder( QueryEngine queryEngine, Supplier domainModelAccess, - ServiceRegistry serviceRegistry) { + ServiceRegistry serviceRegistry, + LiteralHandlingMode criteriaLiteralHandlingMode) { this.queryEngine = queryEngine; this.domainModelAccess = domainModelAccess; this.serviceRegistry = serviceRegistry; + this.criteriaLiteralHandlingMode = criteriaLiteralHandlingMode; } @Override @@ -573,17 +580,17 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { @Override public SqmExpression sum(Expression x, N y) { - return createSqmArithmeticNode( BinaryArithmeticOperator.ADD, (SqmExpression) x, literal( y ) ); + return createSqmArithmeticNode( BinaryArithmeticOperator.ADD, (SqmExpression) x, value( y ) ); } @Override public SqmExpression sum(N x, Expression y) { - return createSqmArithmeticNode( BinaryArithmeticOperator.ADD, literal( x ), (SqmExpression) y ); + return createSqmArithmeticNode( BinaryArithmeticOperator.ADD, value( x ), (SqmExpression) y ); } @Override public SqmExpression prod(Expression x, Expression y) { - return createSqmArithmeticNode( BinaryArithmeticOperator.ADD, literal( x ), (SqmExpression) y ); + return createSqmArithmeticNode( BinaryArithmeticOperator.ADD, value( x ), (SqmExpression) y ); } @Override @@ -610,7 +617,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { return createSqmArithmeticNode( BinaryArithmeticOperator.SUBTRACT, (SqmExpression) x, - literal( y ) + value( y ) ); } @@ -618,7 +625,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { public SqmExpression diff(N x, Expression y) { return createSqmArithmeticNode( BinaryArithmeticOperator.SUBTRACT, - literal( x ), + value( x ), (SqmExpression) y ); } @@ -637,7 +644,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { return createSqmArithmeticNode( BinaryArithmeticOperator.QUOT, (SqmExpression) x, - literal( y ) + value( y ) ); } @@ -645,7 +652,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { public SqmExpression quot(Number x, Expression y) { return createSqmArithmeticNode( BinaryArithmeticOperator.QUOT, - literal( x ), + value( x ), (SqmExpression) y ); } @@ -664,7 +671,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { return createSqmArithmeticNode( BinaryArithmeticOperator.MODULO, (SqmExpression) x, - literal( y ) + value( y ) ); } @@ -672,7 +679,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { public SqmExpression mod(Integer x, Expression y) { return createSqmArithmeticNode( BinaryArithmeticOperator.MODULO, - literal( x ), + value( x ), (SqmExpression) y ); } @@ -882,7 +889,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { @Override public SqmExpression concat(Expression x, String y) { final SqmExpression xSqmExpression = (SqmExpression) x; - final SqmExpression ySqmExpression = literal( y ); + final SqmExpression ySqmExpression = value( y ); //noinspection unchecked return getFunctionDescriptor( "concat" ).generateSqmExpression( asList( xSqmExpression, ySqmExpression ), @@ -898,7 +905,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { @Override public SqmExpression concat(String x, Expression y) { - final SqmExpression xSqmExpression = literal( x ); + final SqmExpression xSqmExpression = value( x ); final SqmExpression ySqmExpression = (SqmExpression) y; //noinspection unchecked return getFunctionDescriptor( "concat" ).generateSqmExpression( @@ -915,8 +922,8 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { @Override public SqmExpression concat(String x, String y) { - final SqmExpression xSqmExpression = literal( x ); - final SqmExpression ySqmExpression = literal( y ); + final SqmExpression xSqmExpression = value( x ); + final SqmExpression ySqmExpression = value( y ); //noinspection unchecked return getFunctionDescriptor( "concat" ).generateSqmExpression( asList( xSqmExpression, ySqmExpression ), @@ -958,7 +965,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { public SqmFunction substring(Expression source, int from) { return createSubstringNode( (SqmExpression) source, - literal( from ), + value( from ), null ); } @@ -976,8 +983,8 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { public SqmFunction substring(Expression source, int from, int len) { return createSubstringNode( (SqmExpression) source, - literal( from ), - literal( len ) + value( from ), + value( len ) ); } @@ -1148,7 +1155,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { public SqmFunction locate(Expression source, String pattern) { return createLocateFunctionNode( (SqmExpression) source, - literal( pattern ), + value( pattern ), null ); } @@ -1166,8 +1173,8 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { public SqmFunction locate(Expression source, String pattern, int startPosition) { return createLocateFunctionNode( (SqmExpression) source, - literal( pattern ), - literal( startPosition ) + value( pattern ), + value( startPosition ) ); } @@ -1280,6 +1287,27 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { throw new NotYetImplementedFor6Exception(); } + @Override + public SqmExpression value(T value) { + if ( criteriaLiteralHandlingMode == LiteralHandlingMode.INLINE ) { + return literal( value ); + } + else { + final BasicType basicType; + if ( value == null ) { + basicType = null; + } + else { + basicType = getTypeConfiguration().getBasicTypeForJavaType( value.getClass() ); + } + return new JpaCriteriaParameter<>( + basicType, + value, + this + ); + } + } + @Override public > SqmExpression> values(C collection) { throw new NotYetImplementedFor6Exception(); @@ -1326,7 +1354,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { @Override public JpaCoalesce coalesce(Expression x, Y y) { - return coalesce( x, literal( y ) ); + return coalesce( x, value( y ) ); } @Override @@ -1338,7 +1366,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { @Override public SqmExpression nullif(Expression x, Y y) { //noinspection unchecked - return createNullifFunctionNode( (SqmExpression) x, literal( y ) ); + return createNullifFunctionNode( (SqmExpression) x, value( y ) ); } private SqmExpression createNullifFunctionNode(SqmExpression first, SqmExpression second) { @@ -1498,8 +1526,8 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { public > SqmPredicate between(Expression value, Y lower, Y upper) { return new SqmBetweenPredicate( (SqmExpression) value, - literal( lower ), - literal( upper ), + value( lower ), + value( upper ), false, this ); @@ -1520,7 +1548,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.EQUAL, - literal( y ), + value( y ), this ); } @@ -1540,7 +1568,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.NOT_EQUAL, - literal( y ), + value( y ), this ); } @@ -1560,7 +1588,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.GREATER_THAN, - literal( y ), + value( y ), this ); } @@ -1580,7 +1608,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.GREATER_THAN_OR_EQUAL, - literal( y ), + value( y ), this ); } @@ -1600,7 +1628,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.LESS_THAN, - literal( y ), + value( y ), this ); } @@ -1620,7 +1648,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.LESS_THAN_OR_EQUAL, - literal( y ), + value( y ), this ); } @@ -1640,7 +1668,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.GREATER_THAN, - literal( y ), + value( y ), this ); } @@ -1660,7 +1688,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.GREATER_THAN_OR_EQUAL, - literal( y ), + value( y ), this ); } @@ -1680,7 +1708,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.LESS_THAN, - literal( y ), + value( y ), this ); } @@ -1700,7 +1728,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.LESS_THAN_OR_EQUAL, - literal( y ), + value( y ), this ); } @@ -1748,7 +1776,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { public SqmPredicate like(Expression searchString, String pattern) { return new SqmLikePredicate( (SqmExpression) searchString, - literal( pattern ), + value( pattern ), this ); } @@ -1777,7 +1805,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { public SqmPredicate like(Expression searchString, String pattern, Expression escapeChar) { return new SqmLikePredicate( (SqmExpression) searchString, - literal( pattern ), + value( pattern ), (SqmExpression) escapeChar, this ); @@ -1787,7 +1815,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext { public SqmPredicate like(Expression searchString, String pattern, char escapeChar) { return new SqmLikePredicate( (SqmExpression) searchString, - literal( pattern ), + value( pattern ), literal( escapeChar ), this ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmTreePrinter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmTreePrinter.java index e7a9adc1cc..2d46bf77f5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmTreePrinter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmTreePrinter.java @@ -6,6 +6,7 @@ */ package org.hibernate.query.sqm.internal; +import java.util.List; import java.util.Locale; import org.hibernate.NotYetImplementedFor6Exception; @@ -36,6 +37,7 @@ import org.hibernate.query.sqm.tree.expression.SqmCaseSearched; import org.hibernate.query.sqm.tree.expression.SqmCaseSimple; import org.hibernate.query.sqm.tree.expression.SqmCastTarget; import org.hibernate.query.sqm.tree.expression.SqmCoalesce; +import org.hibernate.query.sqm.tree.expression.SqmCollate; import org.hibernate.query.sqm.tree.expression.SqmCollectionSize; import org.hibernate.query.sqm.tree.expression.SqmDistinct; import org.hibernate.query.sqm.tree.expression.SqmDurationUnit; @@ -81,10 +83,9 @@ import org.hibernate.query.sqm.tree.predicate.SqmMemberOfPredicate; import org.hibernate.query.sqm.tree.predicate.SqmNegatedPredicate; import org.hibernate.query.sqm.tree.predicate.SqmNullnessPredicate; import org.hibernate.query.sqm.tree.predicate.SqmOrPredicate; +import org.hibernate.query.sqm.tree.predicate.SqmPredicate; import org.hibernate.query.sqm.tree.predicate.SqmWhereClause; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiation; -import org.hibernate.query.sqm.tree.select.SqmGroupByClause; -import org.hibernate.query.sqm.tree.select.SqmHavingClause; import org.hibernate.query.sqm.tree.select.SqmOrderByClause; import org.hibernate.query.sqm.tree.select.SqmQuerySpec; import org.hibernate.query.sqm.tree.select.SqmSelectClause; @@ -394,21 +395,18 @@ public class SqmTreePrinter implements SemanticQueryWalker { // query-spec @Override - public Object visitQuerySpec(SqmQuerySpec querySpec) { + public Object visitQuerySpec(SqmQuerySpec querySpec) { processStanza( "query-spec", () -> { visitSelectClause( querySpec.getSelectClause() ); - visitFromClause( querySpec.getFromClause() ); - - visitGroupByClause( querySpec.getGroupByClause() ); - visitHavingClause( querySpec.getHavingClause() ); - visitWhereClause( querySpec.getWhereClause() ); - visitOrderByClause( querySpec.getOrderByClause() ); + visitGroupByClause( querySpec.getGroupByClauseExpressions() ); + visitHavingClause( querySpec.getHavingClausePredicate() ); + visitOrderByClause( querySpec.getOrderByClause() ); visitLimitExpression( querySpec.getLimitExpression() ); visitOffsetExpression( querySpec.getOffsetExpression() ); } @@ -418,11 +416,11 @@ public class SqmTreePrinter implements SemanticQueryWalker { } @Override - public Object visitGroupByClause(SqmGroupByClause clause) { - if ( clause != null ) { + public Object visitGroupByClause(List> groupByClauseExpressions) { + if ( groupByClauseExpressions != null && !groupByClauseExpressions.isEmpty() ) { processStanza( "group-by", - () -> clause.visitGroupings( this::visitGrouping ) + () -> groupByClauseExpressions.forEach( e -> e.accept( this ) ) ); } @@ -430,21 +428,11 @@ public class SqmTreePrinter implements SemanticQueryWalker { } @Override - public Object visitGrouping(SqmGroupByClause.SqmGrouping grouping) { - processStanza( - "grouping", - () -> grouping.getExpression().accept( this ) - ); - - return null; - } - - @Override - public Object visitHavingClause(SqmHavingClause clause) { - if ( clause != null ) { + public Object visitHavingClause(SqmPredicate predicate) { + if ( predicate != null ) { processStanza( "having", - () -> clause.getPredicate().accept( this ) + () -> predicate.accept( this ) ); } @@ -912,6 +900,11 @@ public class SqmTreePrinter implements SemanticQueryWalker { return null; } + @Override + public Object visitCollate(SqmCollate sqmCollate) { + return null; + } + @Override public Object visitBinaryArithmeticExpression(SqmBinaryArithmetic expression) { return null; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java index bee12af246..b02c435281 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java @@ -231,7 +231,7 @@ public class SqmUtil { final JdbcParameter jdbcParameter = jdbcParams.get( i ); jdbcParameterBindings.addBinding( jdbcParameter, - new JdbcParameterBindingImpl( StandardBasicTypes.SERIALIZABLE, null ) + new JdbcParameterBindingImpl( null, null ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/BaseSemanticQueryWalker.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/BaseSemanticQueryWalker.java index e473af03dd..e7d489ae37 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/BaseSemanticQueryWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/BaseSemanticQueryWalker.java @@ -6,6 +6,8 @@ */ package org.hibernate.query.sqm.spi; +import java.util.List; + import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.tree.cte.SqmCteConsumer; @@ -33,6 +35,7 @@ import org.hibernate.query.sqm.tree.expression.SqmCaseSearched; import org.hibernate.query.sqm.tree.expression.SqmCaseSimple; import org.hibernate.query.sqm.tree.expression.SqmCastTarget; import org.hibernate.query.sqm.tree.expression.SqmCoalesce; +import org.hibernate.query.sqm.tree.expression.SqmCollate; import org.hibernate.query.sqm.tree.expression.SqmCollectionSize; import org.hibernate.query.sqm.tree.expression.SqmDurationUnit; import org.hibernate.query.sqm.tree.expression.SqmEnumLiteral; @@ -77,10 +80,9 @@ import org.hibernate.query.sqm.tree.predicate.SqmMemberOfPredicate; import org.hibernate.query.sqm.tree.predicate.SqmNegatedPredicate; import org.hibernate.query.sqm.tree.predicate.SqmNullnessPredicate; import org.hibernate.query.sqm.tree.predicate.SqmOrPredicate; +import org.hibernate.query.sqm.tree.predicate.SqmPredicate; import org.hibernate.query.sqm.tree.predicate.SqmWhereClause; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiation; -import org.hibernate.query.sqm.tree.select.SqmGroupByClause; -import org.hibernate.query.sqm.tree.select.SqmHavingClause; import org.hibernate.query.sqm.tree.select.SqmOrderByClause; import org.hibernate.query.sqm.tree.select.SqmQuerySpec; import org.hibernate.query.sqm.tree.select.SqmSelectClause; @@ -195,7 +197,7 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker querySpec) { visitFromClause( querySpec.getFromClause() ); visitSelectClause( querySpec.getSelectClause() ); visitWhereClause( querySpec.getWhereClause() ); @@ -453,21 +455,16 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker> groupByClauseExpressions) { + if ( groupByClauseExpressions != null ) { + groupByClauseExpressions.forEach( e -> e.accept( this ) ); + } + return groupByClauseExpressions; } @Override - public Object visitGrouping(SqmGroupByClause.SqmGrouping grouping) { - grouping.getExpression().accept( this ); - return grouping; - } - - @Override - public Object visitHavingClause(SqmHavingClause clause) { - clause.getPredicate().accept( this ); - return clause; + public Object visitHavingClause(SqmPredicate sqmPredicate) { + return sqmPredicate.accept( this ); } @Override @@ -587,15 +584,21 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker literal) { return literal; } @Override - public Object visitTuple(SqmTuple sqmTuple) { + public Object visitTuple(SqmTuple sqmTuple) { return sqmTuple; } + @Override + public Object visitCollate(SqmCollate sqmCollate) { + sqmCollate.getExpression().accept( this ); + return sqmCollate; + } + @Override public Object visitBinaryArithmeticExpression(SqmBinaryArithmetic expression) { return expression; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index a1770c4635..eabe5a8e3f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -32,6 +32,7 @@ import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.PluralAttributeMapping; +import org.hibernate.metamodel.mapping.internal.EmbeddedCollectionPart; import org.hibernate.metamodel.mapping.internal.EntityCollectionPart; import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType; import org.hibernate.metamodel.model.domain.AllowableParameterType; @@ -87,6 +88,7 @@ import org.hibernate.query.sqm.tree.expression.SqmByUnit; import org.hibernate.query.sqm.tree.expression.SqmCaseSearched; import org.hibernate.query.sqm.tree.expression.SqmCaseSimple; import org.hibernate.query.sqm.tree.expression.SqmCastTarget; +import org.hibernate.query.sqm.tree.expression.SqmCollate; import org.hibernate.query.sqm.tree.expression.SqmDistinct; import org.hibernate.query.sqm.tree.expression.SqmDurationUnit; import org.hibernate.query.sqm.tree.expression.SqmEnumLiteral; @@ -105,6 +107,7 @@ import org.hibernate.query.sqm.tree.expression.SqmPositionalParameter; import org.hibernate.query.sqm.tree.expression.SqmStar; import org.hibernate.query.sqm.tree.expression.SqmToDuration; import org.hibernate.query.sqm.tree.expression.SqmTrimSpecification; +import org.hibernate.query.sqm.tree.expression.SqmTuple; import org.hibernate.query.sqm.tree.expression.SqmUnaryOperation; import org.hibernate.query.sqm.tree.from.SqmAttributeJoin; import org.hibernate.query.sqm.tree.from.SqmCrossJoin; @@ -128,6 +131,7 @@ import org.hibernate.query.sqm.tree.predicate.SqmMemberOfPredicate; import org.hibernate.query.sqm.tree.predicate.SqmNegatedPredicate; import org.hibernate.query.sqm.tree.predicate.SqmNullnessPredicate; import org.hibernate.query.sqm.tree.predicate.SqmOrPredicate; +import org.hibernate.query.sqm.tree.predicate.SqmPredicate; import org.hibernate.query.sqm.tree.predicate.SqmWhereClause; import org.hibernate.query.sqm.tree.select.SqmOrderByClause; import org.hibernate.query.sqm.tree.select.SqmQuerySpec; @@ -156,6 +160,7 @@ import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; import org.hibernate.sql.ast.tree.expression.CastTarget; +import org.hibernate.sql.ast.tree.expression.Collate; import org.hibernate.sql.ast.tree.expression.Distinct; import org.hibernate.sql.ast.tree.expression.Duration; import org.hibernate.sql.ast.tree.expression.DurationUnit; @@ -166,6 +171,7 @@ import org.hibernate.sql.ast.tree.expression.Format; import org.hibernate.sql.ast.tree.expression.JdbcLiteral; import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.expression.QueryLiteral; +import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.Star; import org.hibernate.sql.ast.tree.expression.TrimSpecification; import org.hibernate.sql.ast.tree.expression.UnaryOperation; @@ -180,7 +186,6 @@ import org.hibernate.sql.ast.tree.predicate.InListPredicate; import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; import org.hibernate.sql.ast.tree.predicate.Junction; import org.hibernate.sql.ast.tree.predicate.LikePredicate; -import org.hibernate.sql.ast.tree.predicate.MemberOfPredicate; import org.hibernate.sql.ast.tree.predicate.NegatedPredicate; import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; import org.hibernate.sql.ast.tree.predicate.Predicate; @@ -404,7 +409,7 @@ public abstract class BaseSqmToSqlAstConverter } @Override - public QuerySpec visitQuerySpec(SqmQuerySpec sqmQuerySpec) { + public QuerySpec visitQuerySpec(SqmQuerySpec sqmQuerySpec) { final QuerySpec sqlQuerySpec = new QuerySpec( processingStateStack.isEmpty(), sqmQuerySpec.getFromClause().getNumberOfRoots() ); additionalRestrictions = null; @@ -444,8 +449,10 @@ public abstract class BaseSqmToSqlAstConverter sqlQuerySpec.applyPredicate( additionalRestrictions ); } - // todo : group-by - // todo : having + sqlQuerySpec.setGroupByClauseExpressions( visitGroupByClause( sqmQuerySpec.getGroupByClauseExpressions() ) ); + if ( sqmQuerySpec.getHavingClausePredicate() != null ) { + sqlQuerySpec.setHavingClauseRestrictions( visitHavingClause( sqmQuerySpec.getHavingClausePredicate() ) ); + } if ( sqmQuerySpec.getOrderByClause() != null ) { currentClauseStack.push( Clause.ORDER ); @@ -493,6 +500,38 @@ public abstract class BaseSqmToSqlAstConverter } } + @Override + public List visitGroupByClause(List> groupByClauseExpressions) { + if ( !groupByClauseExpressions.isEmpty() ) { + currentClauseStack.push( Clause.GROUP ); + try { + List expressions = new ArrayList<>( groupByClauseExpressions.size() ); + for ( SqmExpression groupByClauseExpression : groupByClauseExpressions ) { + expressions.add( (Expression) groupByClauseExpression.accept( this ) ); + } + return expressions; + } + finally { + currentClauseStack.pop(); + } + } + return Collections.emptyList(); + } + + @Override + public Predicate visitHavingClause(SqmPredicate sqmPredicate) { + if ( sqmPredicate == null ) { + return null; + } + currentClauseStack.push( Clause.HAVING ); + try { + return (Predicate) sqmPredicate.accept( this ); + } + finally { + currentClauseStack.pop(); + } + } + @Override public Void visitOrderByClause(SqmOrderByClause orderByClause) { super.visitOrderByClause( orderByClause ); @@ -503,7 +542,7 @@ public abstract class BaseSqmToSqlAstConverter public SortSpecification visitSortSpecification(SqmSortSpecification sortSpecification) { return new SortSpecification( (Expression) sortSpecification.getSortExpression().accept( this ), - sortSpecification.getCollation(), + null, sortSpecification.getSortOrder(), sortSpecification.getNullPrecedence() ); @@ -1001,7 +1040,7 @@ public abstract class BaseSqmToSqlAstConverter // General expressions @Override - public Expression visitLiteral(SqmLiteral literal) { + public Expression visitLiteral(SqmLiteral literal) { if ( literal instanceof SqmLiteralNull ) { return new QueryLiteral<>( null, (BasicValuedMapping) inferableTypeAccessStack.getCurrent().get() ); } @@ -1196,6 +1235,24 @@ public abstract class BaseSqmToSqlAstConverter return consumeSqmParameter( supplier.get() ); } + @Override + public Object visitTuple(SqmTuple sqmTuple) { + final List> groupedExpressions = sqmTuple.getGroupedExpressions(); + final int size = groupedExpressions.size(); + final List expressions = new ArrayList<>( size ); + for ( int i = 0; i < size; i++ ) { + expressions.add( (Expression) groupedExpressions.get( i ).accept( this ) ); + } + return new SqlTuple( expressions, null ); + } + + @Override + public Object visitCollate(SqmCollate sqmCollate) { + return new Collate( + (Expression) sqmCollate.getExpression().accept( this ), + sqmCollate.getCollation() + ); + } @Override public Expression visitFunction(SqmFunction sqmFunction) { @@ -2209,7 +2266,7 @@ public abstract class BaseSqmToSqlAstConverter } @Override - public MemberOfPredicate visitMemberOfPredicate(SqmMemberOfPredicate predicate) { + public InSubQueryPredicate visitMemberOfPredicate(SqmMemberOfPredicate predicate) { final SqmPath pluralPath = predicate.getPluralPath(); final PluralAttributeMapping mappingModelExpressable = (PluralAttributeMapping) determineValueMapping(pluralPath); @@ -2217,6 +2274,10 @@ public abstract class BaseSqmToSqlAstConverter inferableTypeAccessStack.push( () -> ( (EntityCollectionPart) mappingModelExpressable.getElementDescriptor() ).getKeyTargetMatchPart() ); } + else if ( mappingModelExpressable.getElementDescriptor() instanceof EmbeddedCollectionPart ) { + inferableTypeAccessStack.push( + () -> mappingModelExpressable.getElementDescriptor() ); + } else { inferableTypeAccessStack.push( () -> mappingModelExpressable ); } @@ -2229,15 +2290,15 @@ public abstract class BaseSqmToSqlAstConverter inferableTypeAccessStack.pop(); } - return new MemberOfPredicate( + return new InSubQueryPredicate( lhs, - predicate.isNegated(), - createMemberOfSubQuery( pluralPath, mappingModelExpressable ) + createMemberOfSubQuery( pluralPath, mappingModelExpressable ), + predicate.isNegated() ); } private QuerySpec createMemberOfSubQuery(SqmPath pluralPath, PluralAttributeMapping mappingModelExpressable) { - final QuerySpec querySpec = new QuerySpec( true ); + final QuerySpec querySpec = new QuerySpec( false ); processingStateStack.push( new SqlAstQuerySpecProcessingStateImpl( querySpec, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EmbeddableValuedPathInterpretation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EmbeddableValuedPathInterpretation.java index 719cb643ae..fac944925a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EmbeddableValuedPathInterpretation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EmbeddableValuedPathInterpretation.java @@ -54,10 +54,10 @@ public class EmbeddableValuedPathInterpretation extends AbstractSqmPathInterp ); } - private final Expression sqlExpression; + private final SqlTuple sqlExpression; public EmbeddableValuedPathInterpretation( - Expression sqlExpression, + SqlTuple sqlExpression, SqmEmbeddedValuedSimplePath sqmPath, EmbeddableValuedModelPart mapping, TableGroup tableGroup) { @@ -65,7 +65,7 @@ public class EmbeddableValuedPathInterpretation extends AbstractSqmPathInterp this.sqlExpression = sqlExpression; } - public Expression getSqlExpression() { + public SqlTuple getSqlExpression() { return sqlExpression; } @@ -81,20 +81,11 @@ public class EmbeddableValuedPathInterpretation extends AbstractSqmPathInterp @Override public void visitColumnReferences(Consumer columnReferenceConsumer) { - if ( sqlExpression instanceof ColumnReference ) { - columnReferenceConsumer.accept( (ColumnReference) sqlExpression ); - } - else if ( sqlExpression instanceof SqlTuple ) { - final SqlTuple sqlTuple = (SqlTuple) sqlExpression; - for ( Expression expression : sqlTuple.getExpressions() ) { - if ( !( expression instanceof ColumnReference ) ) { - throw new IllegalArgumentException( "Expecting ColumnReference, found : " + expression ); - } - columnReferenceConsumer.accept( (ColumnReference) expression ); + for ( Expression expression : sqlExpression.getExpressions() ) { + if ( !( expression instanceof ColumnReference ) ) { + throw new IllegalArgumentException( "Expecting ColumnReference, found : " + expression ); } - } - else { - // error or warning... + columnReferenceConsumer.accept( (ColumnReference) expression ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/NonAggregatedCompositeValuedPathInterpretation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/NonAggregatedCompositeValuedPathInterpretation.java index a3a7ad61b5..a96e9a2b33 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/NonAggregatedCompositeValuedPathInterpretation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/NonAggregatedCompositeValuedPathInterpretation.java @@ -13,6 +13,7 @@ import org.hibernate.query.sqm.tree.domain.NonAggregatedCompositeSimplePath; import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.from.TableGroup; /** @@ -30,7 +31,7 @@ public class NonAggregatedCompositeValuedPathInterpretation extends AbstractS final NonAggregatedIdentifierMappingImpl mapping = (NonAggregatedIdentifierMappingImpl) tableGroup.getModelPart() .findSubPart( sqmPath.getReferencedPathSource().getPathName(), null ); - return new NonAggregatedCompositeValuedPathInterpretation( + return new NonAggregatedCompositeValuedPathInterpretation<>( mapping.toSqlExpression( tableGroup, converter.getCurrentClauseStack().getCurrent(), @@ -43,10 +44,10 @@ public class NonAggregatedCompositeValuedPathInterpretation extends AbstractS ); } - private final Expression sqlExpression; + private final SqlTuple sqlExpression; public NonAggregatedCompositeValuedPathInterpretation( - Expression sqlExpression, + SqlTuple sqlExpression, SqmPath sqmPath, ModelPart mapping, TableGroup tableGroup) { @@ -54,6 +55,10 @@ public class NonAggregatedCompositeValuedPathInterpretation extends AbstractS this.sqlExpression = sqlExpression; } + public SqlTuple getSqlExpression() { + return sqlExpression; + } + @Override public void accept(SqlAstWalker sqlTreeWalker) { sqlExpression.accept( sqlTreeWalker ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/SqmParameterInterpretation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/SqmParameterInterpretation.java index 46b24a8e73..e1f34d0258 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/SqmParameterInterpretation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/SqmParameterInterpretation.java @@ -9,6 +9,7 @@ package org.hibernate.query.sqm.sql.internal; import java.util.List; import java.util.function.Function; +import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.metamodel.model.domain.AllowableParameterType; import org.hibernate.query.SemanticException; @@ -49,9 +50,13 @@ public class SqmParameterInterpretation implements Expression, DomainResultProdu assert jdbcParameters != null; assert jdbcParameters.size() > 0; - this.resolvedExpression = jdbcParameters.size() == 1 - ? jdbcParameters.get( 0 ) - : new SqlTuple( jdbcParameters, valueMapping ); + this.resolvedExpression = valueMapping instanceof EmbeddableValuedModelPart + ? new SqlTuple( jdbcParameters, valueMapping ) + : jdbcParameters.get( 0 ); + } + + public Expression getResolvedExpression() { + return resolvedExpression; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/JpaCriteriaParameter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/JpaCriteriaParameter.java index c73777c933..50156928ec 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/JpaCriteriaParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/JpaCriteriaParameter.java @@ -30,6 +30,7 @@ public class JpaCriteriaParameter extends AbstractSqmExpression implements SqmParameter, QueryParameterImplementor, DomainResultProducer { private final String name; + private final T value; private boolean allowsMultiValuedBinding; public JpaCriteriaParameter( @@ -39,6 +40,7 @@ public class JpaCriteriaParameter NodeBuilder nodeBuilder) { super( type, nodeBuilder ); this.name = name; + this.value = null; this.allowsMultiValuedBinding = allowsMultiValuedBinding; } public JpaCriteriaParameter( @@ -48,11 +50,21 @@ public class JpaCriteriaParameter this( null, type, allowsMultiValuedBinding, nodeBuilder ); } + public JpaCriteriaParameter(AllowableParameterType type, T value, NodeBuilder criteriaBuilder) { + super( type, criteriaBuilder ); + this.name = null; + this.value = value; + } + @Override public String getName() { return name; } + public T getValue() { + return value; + } + @Override public Integer getPosition() { // for criteria anyway, these cannot be positional diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAny.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAny.java index 973e4ea6c3..502abf2bb4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAny.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAny.java @@ -18,8 +18,8 @@ public class SqmAny extends AbstractSqmExpression { private final SqmSubQuery subquery; - public SqmAny(SqmSubQuery subquery, SqmExpressable type, NodeBuilder criteriaBuilder) { - super(type, criteriaBuilder); + public SqmAny(SqmSubQuery subquery, NodeBuilder criteriaBuilder) { + super( subquery.getNodeType(), criteriaBuilder ); this.subquery = subquery; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSearched.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSearched.java index 2c79537bbe..21a9297a7b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSearched.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSearched.java @@ -116,7 +116,7 @@ public class SqmCaseSearched @Override public SqmCaseSearched when(Expression condition, R result) { - when( nodeBuilder().wrap( condition ), nodeBuilder().literal( result ) ); + when( nodeBuilder().wrap( condition ), nodeBuilder().value( result ) ); return this; } @@ -129,7 +129,7 @@ public class SqmCaseSearched @Override public JpaExpression otherwise(R result) { - otherwise( nodeBuilder().literal( result ) ); + otherwise( nodeBuilder().value( result ) ); return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSimple.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSimple.java index f1dbbd1a5a..5268efaad7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSimple.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSimple.java @@ -129,7 +129,7 @@ public class SqmCaseSimple @Override public JpaSimpleCase when(T condition, R result) { - when( nodeBuilder().literal( condition ), nodeBuilder().literal( result ) ); + when( nodeBuilder().literal( condition ), nodeBuilder().value( result ) ); return this; } @@ -142,7 +142,7 @@ public class SqmCaseSimple @Override public JpaSimpleCase otherwise(R result) { - otherwise( nodeBuilder().literal( result ) ); + otherwise( nodeBuilder().value( result ) ); return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCoalesce.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCoalesce.java index b3492c086f..3e36e7dde2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCoalesce.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCoalesce.java @@ -69,7 +69,7 @@ public class SqmCoalesce extends AbstractSqmExpression implements JpaCoale @Override public SqmCoalesce value(T value) { - value( nodeBuilder().literal( value ) ); + value( nodeBuilder().value( value ) ); return this; } @@ -91,7 +91,7 @@ public class SqmCoalesce extends AbstractSqmExpression implements JpaCoale @SuppressWarnings("unchecked") public SqmCoalesce values(T... values) { for ( T value : values ) { - value( nodeBuilder().literal( value ) ); + value( nodeBuilder().value( value ) ); } return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCollate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCollate.java new file mode 100644 index 0000000000..05c2e1dad4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCollate.java @@ -0,0 +1,38 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.tree.expression; + +import org.hibernate.query.sqm.SemanticQueryWalker; + +/** + * @author Christian Beikov + */ +public class SqmCollate extends AbstractSqmExpression { + + private final SqmExpression expression; + private final String collation; + + public SqmCollate(SqmExpression expression, String collation) { + super( expression.getNodeType(), expression.nodeBuilder() ); + assert !(expression instanceof SqmTuple); + this.expression = expression; + this.collation = collation; + } + + public SqmExpression getExpression() { + return expression; + } + + public String getCollation() { + return collation; + } + + @Override + public X accept(SemanticQueryWalker walker) { + return walker.visitCollate( this ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEvery.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEvery.java index 4e784cee0b..16ce953fa1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEvery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEvery.java @@ -18,8 +18,8 @@ public class SqmEvery extends AbstractSqmExpression { private final SqmSubQuery subquery; - public SqmEvery(SqmSubQuery subquery, SqmExpressable type, NodeBuilder criteriaBuilder) { - super(type, criteriaBuilder); + public SqmEvery(SqmSubQuery subquery, NodeBuilder criteriaBuilder) { + super( subquery.getNodeType(), criteriaBuilder ); this.subquery = subquery; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJpaCriteriaParameterWrapper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJpaCriteriaParameterWrapper.java index 62f48b5310..c6b3df17e5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJpaCriteriaParameterWrapper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJpaCriteriaParameterWrapper.java @@ -11,6 +11,7 @@ import java.util.function.Consumer; import org.hibernate.metamodel.model.domain.AllowableParameterType; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; +import org.hibernate.query.sqm.SqmExpressable; import org.hibernate.query.sqm.tree.select.SqmSelectableNode; import org.hibernate.sql.ast.tree.expression.JdbcParameter; @@ -50,8 +51,9 @@ public class SqmJpaCriteriaParameterWrapper @Override public AllowableParameterType getNodeType() { - if ( super.getNodeType() instanceof AllowableParameterType ) { - return ( (AllowableParameterType) super.getNodeType() ); + SqmExpressable nodeType = super.getNodeType(); + if ( nodeType == null || nodeType instanceof AllowableParameterType ) { + return ( (AllowableParameterType) nodeType ); } throw new IllegalStateException( "Expecting AllowableParameterType as node type" ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInListPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInListPredicate.java index 47388967e3..e1e4dd90f3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInListPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInListPredicate.java @@ -74,11 +74,11 @@ public class SqmInListPredicate extends AbstractNegatableSqmPredicate impleme public SqmInPredicate value(Object value) { if ( value instanceof Collection ) { ( (Collection) value ).forEach( - v -> addExpression( nodeBuilder().literal( v ) ) + v -> addExpression( nodeBuilder().value( v ) ) ); return this; } - addExpression( nodeBuilder().literal( value ) ); + addExpression( nodeBuilder().value( value ) ); return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmGroupByClause.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmGroupByClause.java deleted file mode 100644 index e7499435f2..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmGroupByClause.java +++ /dev/null @@ -1,88 +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.sqm.tree.select; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -import org.hibernate.query.sqm.tree.expression.SqmExpression; - -/** - * Models the GROUP-BY clause of a SqmQuerySpec - * - * @author Steve Ebersole - */ -public class SqmGroupByClause { - private List groupings; - - public List getGroupings() { - return groupings; - } - - public void visitGroupings(Consumer consumer) { - if ( groupings != null ) { - groupings.forEach( consumer ); - } - } - - public void setGroupings(List groupings) { - this.groupings = groupings; - } - - public void addGrouping(SqmGrouping grouping) { - if ( groupings == null ) { - groupings = new ArrayList<>(); - } - - groupings.add( grouping ); - } - - public void addGrouping(SqmExpression groupExpression) { - addGrouping( new SqmGrouping( groupExpression, null ) ); - } - - public void addGrouping(SqmExpression groupExpression, String collation) { - addGrouping( new SqmGrouping( groupExpression, collation ) ); - } - - public void clearGroupings() { - if ( groupings != null ) { - groupings.clear(); - } - } - - public static class SqmGrouping { - private SqmExpression expression; - // todo (6.0) : special type besides String? - private String collation; - - public SqmGrouping() { - } - - public SqmGrouping(SqmExpression expression, String collation) { - this.expression = expression; - this.collation = collation; - } - - public SqmExpression getExpression() { - return expression; - } - - public void setExpression(SqmExpression expression) { - this.expression = expression; - } - - public String getCollation() { - return collation; - } - - public void setCollation(String collation) { - this.collation = collation; - } - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmHavingClause.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmHavingClause.java deleted file mode 100644 index 5712838abc..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmHavingClause.java +++ /dev/null @@ -1,33 +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.sqm.tree.select; - -import org.hibernate.query.sqm.tree.predicate.SqmPredicate; - -/** - * Models the HAVING clause of a SqmQuerySpec - * - * @author Steve Ebersole - */ -public class SqmHavingClause { - private SqmPredicate predicate; - - public SqmHavingClause() { - } - - public SqmHavingClause(SqmPredicate predicate) { - this.predicate = predicate; - } - - public SqmPredicate getPredicate() { - return predicate; - } - - public void setPredicate(SqmPredicate predicate) { - this.predicate = predicate; - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java index 0bdcbc589f..6f1dcea127 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java @@ -44,13 +44,13 @@ public class SqmQuerySpec implements SqmCteConsumer, SqmNode, SqmFromClauseCo private SqmSelectClause selectClause; private SqmWhereClause whereClause; - private SqmGroupByClause groupByClause; - private SqmHavingClause havingClause; + private List> groupByClauseExpressions = Collections.emptyList(); + private SqmPredicate havingClausePredicate; private SqmOrderByClause orderByClause; - private SqmExpression limitExpression; - private SqmExpression offsetExpression; + private SqmExpression limitExpression; + private SqmExpression offsetExpression; public SqmQuerySpec(NodeBuilder nodeBuilder) { this.nodeBuilder = nodeBuilder; @@ -100,20 +100,22 @@ public class SqmQuerySpec implements SqmCteConsumer, SqmNode, SqmFromClauseCo whereClause.applyPredicate( predicate ); } - public SqmGroupByClause getGroupByClause() { - return groupByClause; + public List> getGroupByClauseExpressions() { + return groupByClauseExpressions; } - public void setGroupByClause(SqmGroupByClause groupByClause) { - this.groupByClause = groupByClause; + public void setGroupByClauseExpressions(List> groupByClauseExpressions) { + this.groupByClauseExpressions = groupByClauseExpressions == null + ? Collections.emptyList() + : groupByClauseExpressions; } - public SqmHavingClause getHavingClause() { - return havingClause; + public SqmPredicate getHavingClausePredicate() { + return havingClausePredicate; } - public void setHavingClause(SqmHavingClause havingClause) { - this.havingClause = havingClause; + public void setHavingClausePredicate(SqmPredicate havingClausePredicate) { + this.havingClausePredicate = havingClausePredicate; } public SqmOrderByClause getOrderByClause() { @@ -124,7 +126,7 @@ public class SqmQuerySpec implements SqmCteConsumer, SqmNode, SqmFromClauseCo this.orderByClause = orderByClause; } - public SqmExpression getLimitExpression() { + public SqmExpression getLimitExpression() { return limitExpression; } @@ -135,7 +137,7 @@ public class SqmQuerySpec implements SqmCteConsumer, SqmNode, SqmFromClauseCo this.limitExpression = limitExpression; } - public SqmExpression getOffsetExpression() { + public SqmExpression getOffsetExpression() { return offsetExpression; } @@ -157,7 +159,7 @@ public class SqmQuerySpec implements SqmCteConsumer, SqmNode, SqmFromClauseCo } @Override - public SqmQuerySpec setDistinct(boolean distinct) { + public SqmQuerySpec setDistinct(boolean distinct) { assert getSelectClause() != null; getSelectClause().makeDistinct( distinct ); return this; @@ -171,7 +173,7 @@ public class SqmQuerySpec implements SqmCteConsumer, SqmNode, SqmFromClauseCo } @Override - public SqmQuerySpec setSelection(JpaSelection selection) { + public SqmQuerySpec setSelection(JpaSelection selection) { assert getSelectClause() != null; // NOTE : this call comes from JPA which inherently supports just a // single (possibly "compound") selection @@ -187,7 +189,7 @@ public class SqmQuerySpec implements SqmCteConsumer, SqmNode, SqmFromClauseCo } @Override - public SqmQuerySpec addRoot(JpaRoot root) { + public SqmQuerySpec addRoot(JpaRoot root) { if ( getFromClause() == null ) { setFromClause( new SqmFromClause() ); } @@ -231,90 +233,48 @@ public class SqmQuerySpec implements SqmCteConsumer, SqmNode, SqmFromClauseCo } @Override - public List getGroupingExpressions() { - if ( getGroupByClause() == null ) { - return Collections.emptyList(); - } - - final List list = new ArrayList<>(); - getGroupByClause().visitGroupings( - sqmGrouping -> list.add( sqmGrouping.getExpression() ) - ); - return list; + public List> getGroupingExpressions() { + return groupByClauseExpressions; } @Override public SqmQuerySpec setGroupingExpressions(List> groupExpressions) { - if ( getGroupByClause() == null ) { - setGroupByClause( new SqmGroupByClause() ); - } - else { - getGroupByClause().clearGroupings(); - } - + this.groupByClauseExpressions = new ArrayList<>( groupExpressions.size() ); for ( JpaExpression groupExpression : groupExpressions ) { - getGroupByClause().addGrouping( (SqmExpression) groupExpression ); + this.groupByClauseExpressions.add( (SqmExpression) groupExpression ); } - return this; } @Override public SqmQuerySpec setGroupingExpressions(JpaExpression... groupExpressions) { - if ( getGroupByClause() == null ) { - setGroupByClause( new SqmGroupByClause() ); - } - else { - getGroupByClause().clearGroupings(); - } - + this.groupByClauseExpressions = new ArrayList<>( groupExpressions.length ); for ( JpaExpression groupExpression : groupExpressions ) { - getGroupByClause().addGrouping( (SqmExpression) groupExpression ); + this.groupByClauseExpressions.add( (SqmExpression) groupExpression ); } - return this; } @Override public SqmPredicate getGroupRestriction() { - if ( getHavingClause() == null ) { - return null; - } - return getHavingClause().getPredicate(); + return havingClausePredicate; } @Override public SqmQuerySpec setGroupRestriction(JpaPredicate restriction) { - if ( getHavingClause() == null ) { - setHavingClause( new SqmHavingClause( (SqmPredicate) restriction ) ); - } - else { - getHavingClause().setPredicate( (SqmPredicate) restriction ); - } + havingClausePredicate = (SqmPredicate) restriction; return this; } @Override public SqmQuerySpec setGroupRestriction(Expression restriction) { - final SqmPredicate predicate = nodeBuilder.wrap( restriction ); - if ( getHavingClause() == null ) { - setHavingClause( new SqmHavingClause( predicate )); - } - else { - getHavingClause().setPredicate( predicate ); - } + havingClausePredicate = nodeBuilder.wrap( restriction ); return this; } @Override public SqmQuerySpec setGroupRestriction(Predicate... restrictions) { - final SqmPredicate predicate = nodeBuilder.wrap( restrictions ); - if ( getHavingClause() == null ) { - setHavingClause( new SqmHavingClause( predicate )); - } - else { - getHavingClause().setPredicate( predicate ); - } + havingClausePredicate = nodeBuilder.wrap( restrictions ); return this; } @@ -341,25 +301,25 @@ public class SqmQuerySpec implements SqmCteConsumer, SqmNode, SqmFromClauseCo @Override @SuppressWarnings("unchecked") - public SqmExpression getLimit() { + public SqmExpression getLimit() { return getLimitExpression(); } @Override public SqmQuerySpec setLimit(JpaExpression limit) { - setLimitExpression( (SqmExpression) limit ); + setLimitExpression( (SqmExpression) limit ); return this; } @Override @SuppressWarnings("unchecked") - public SqmExpression getOffset() { + public SqmExpression getOffset() { return getOffsetExpression(); } @Override - public SqmQuerySpec setOffset(JpaExpression offset) { - setOffsetExpression( (SqmExpression) offset ); + public SqmQuerySpec setOffset(JpaExpression offset) { + setOffsetExpression( (SqmExpression) offset ); return this; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSortSpecification.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSortSpecification.java index 6ef34ffec3..ef689c8ff8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSortSpecification.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSortSpecification.java @@ -17,42 +17,31 @@ import org.hibernate.query.sqm.tree.expression.SqmExpression; */ public class SqmSortSpecification implements JpaOrder { private final SqmExpression sortExpression; - private final String collation; private final SortOrder sortOrder; private NullPrecedence nullPrecedence; public SqmSortSpecification( SqmExpression sortExpression, - String collation, SortOrder sortOrder, NullPrecedence nullPrecedence) { this.sortExpression = sortExpression; - this.collation = collation; this.sortOrder = sortOrder; this.nullPrecedence = nullPrecedence; } public SqmSortSpecification(SqmExpression sortExpression) { - this( sortExpression, null, SortOrder.ASCENDING, null ); + this( sortExpression, SortOrder.ASCENDING, null ); } public SqmSortSpecification(SqmExpression sortExpression, SortOrder sortOrder) { - this( sortExpression, null, sortOrder, null ); - } - - public SqmSortSpecification(SqmExpression sortExpression, SortOrder sortOrder, NullPrecedence nullPrecedence) { - this( sortExpression, null, sortOrder, nullPrecedence ); + this( sortExpression, sortOrder, null ); } public SqmExpression getSortExpression() { return sortExpression; } - public String getCollation() { - return collation; - } - public SortOrder getSortOrder() { return sortOrder; } @@ -75,7 +64,7 @@ public class SqmSortSpecification implements JpaOrder { @Override public JpaOrder reverse() { SortOrder newSortOrder = this.sortOrder == null ? SortOrder.DESCENDING : sortOrder.reverse(); - return new SqmSortSpecification( sortExpression, collation, newSortOrder, nullPrecedence ); + return new SqmSortSpecification( sortExpression, newSortOrder, nullPrecedence ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstWalker.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstWalker.java index 80376cc300..522ff4b179 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstWalker.java @@ -14,6 +14,7 @@ import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; import org.hibernate.sql.ast.tree.expression.CastTarget; +import org.hibernate.sql.ast.tree.expression.Collate; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Distinct; import org.hibernate.sql.ast.tree.expression.Duration; @@ -45,7 +46,6 @@ import org.hibernate.sql.ast.tree.predicate.InListPredicate; import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; import org.hibernate.sql.ast.tree.predicate.Junction; import org.hibernate.sql.ast.tree.predicate.LikePredicate; -import org.hibernate.sql.ast.tree.predicate.MemberOfPredicate; import org.hibernate.sql.ast.tree.predicate.NegatedPredicate; import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; import org.hibernate.sql.ast.tree.predicate.SelfRenderingPredicate; @@ -114,6 +114,8 @@ public interface SqlAstWalker { void visitTuple(SqlTuple tuple); + void visitCollate(Collate collate); + void visitParameter(JdbcParameter jdbcParameter); void visitJdbcLiteral(JdbcLiteral jdbcLiteral); @@ -152,5 +154,4 @@ public interface SqlAstWalker { void visitConversion(Conversion conversion); - void visitMemberOfPredicate(MemberOfPredicate memberOfPredicate); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java index e8f87b67f0..05874552f0 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstWalker.java @@ -6,11 +6,15 @@ */ package org.hibernate.sql.ast.spi; +import java.sql.Timestamp; import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; +import java.util.TimeZone; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NullPrecedence; @@ -18,6 +22,8 @@ import org.hibernate.SortOrder; import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SessionLazyDelegatorBaseImpl; import org.hibernate.internal.FilterJdbcParameter; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.Stack; @@ -28,9 +34,12 @@ import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.internal.BasicValuedCollectionPart; import org.hibernate.persister.entity.Loadable; -import org.hibernate.query.QueryLiteralRendering; +import org.hibernate.query.ComparisonOperator; import org.hibernate.query.UnaryArithmeticOperator; +import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor; import org.hibernate.query.sqm.sql.internal.EmbeddableValuedPathInterpretation; +import org.hibernate.query.sqm.sql.internal.NonAggregatedCompositeValuedPathInterpretation; +import org.hibernate.query.sqm.sql.internal.SqmParameterInterpretation; import org.hibernate.query.sqm.tree.expression.Conversion; import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.SqlAstJoinType; @@ -40,6 +49,7 @@ import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; import org.hibernate.sql.ast.tree.expression.CastTarget; +import org.hibernate.sql.ast.tree.expression.Collate; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Distinct; import org.hibernate.sql.ast.tree.expression.Duration; @@ -52,6 +62,7 @@ import org.hibernate.sql.ast.tree.expression.Format; import org.hibernate.sql.ast.tree.expression.JdbcLiteral; import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.expression.Literal; +import org.hibernate.sql.ast.tree.expression.LiteralAsParameter; import org.hibernate.sql.ast.tree.expression.QueryLiteral; import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression; import org.hibernate.sql.ast.tree.expression.SqlSelectionExpression; @@ -74,7 +85,6 @@ import org.hibernate.sql.ast.tree.predicate.InListPredicate; import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; import org.hibernate.sql.ast.tree.predicate.Junction; import org.hibernate.sql.ast.tree.predicate.LikePredicate; -import org.hibernate.sql.ast.tree.predicate.MemberOfPredicate; import org.hibernate.sql.ast.tree.predicate.NegatedPredicate; import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; import org.hibernate.sql.ast.tree.predicate.Predicate; @@ -84,28 +94,24 @@ import org.hibernate.sql.ast.tree.select.SelectClause; import org.hibernate.sql.ast.tree.select.SortSpecification; import org.hibernate.sql.exec.internal.JdbcParametersImpl; import org.hibernate.sql.exec.spi.JdbcParameterBinder; +import org.hibernate.type.IntegerType; 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; -import static org.hibernate.sql.ast.spi.SqlAppender.CLOSE_PARENTHESIS; -import static org.hibernate.sql.ast.spi.SqlAppender.COMA_SEPARATOR; -import static org.hibernate.sql.ast.spi.SqlAppender.EMPTY_STRING; -import static org.hibernate.sql.ast.spi.SqlAppender.NO_SEPARATOR; -import static org.hibernate.sql.ast.spi.SqlAppender.NULL_KEYWORD; -import static org.hibernate.sql.ast.spi.SqlAppender.OPEN_PARENTHESIS; -import static org.hibernate.sql.ast.spi.SqlAppender.PARAM_MARKER; /** * @author Steve Ebersole */ public abstract class AbstractSqlAstWalker - implements SqlAstWalker, SqlTypeDescriptorIndicators { + implements SqlAstWalker, SqlTypeDescriptorIndicators, SqlAppender { + + private static final QueryLiteral ONE_LITERAL = new QueryLiteral<>( 1, IntegerType.INSTANCE ); // pre-req state private final SessionFactoryImplementor sessionFactory; - private final SqlAppender sqlAppender = this::appendSql; // In-flight state private final StringBuilder sqlBuffer = new StringBuilder(); @@ -118,6 +124,8 @@ public abstract class AbstractSqlAstWalker private final Stack clauseStack = new StandardStack<>(); private final Dialect dialect; + private transient AbstractSqmSelfRenderingFunctionDescriptor castFunction; + private transient LazySession session; public Dialect getDialect() { return dialect; @@ -133,10 +141,75 @@ public abstract class AbstractSqlAstWalker return sessionFactory; } + protected AbstractSqmSelfRenderingFunctionDescriptor castFunction() { + if ( castFunction == null ) { + castFunction = (AbstractSqmSelfRenderingFunctionDescriptor) sessionFactory + .getQueryEngine() + .getSqmFunctionRegistry() + .findFunctionDescriptor( "cast" ); + } + return castFunction; + } + + protected SessionLazyDelegatorBaseImpl getSession() { + if ( session == null ) { + session = new LazySession( sessionFactory ); + } + return session; + } + + /** + * A lazy session implementation that is needed for rendering literals. + * Usually, only the {@link org.hibernate.type.descriptor.WrapperOptions} interface is needed, + * but for creating LOBs, it might be to have a full blown session. + */ + private static class LazySession extends SessionLazyDelegatorBaseImpl { + + private final SessionFactoryImplementor sessionFactory; + private SessionImplementor session; + + public LazySession(SessionFactoryImplementor sessionFactory) { + this.sessionFactory = sessionFactory; + } + + public void cleanup() { + if ( session != null ) { + session.close(); + session = null; + } + } + + @Override + protected SessionImplementor delegate() { + if ( session == null ) { + session = (SessionImplementor) sessionFactory.openTemporarySession(); + } + return session; + } + + @Override + public boolean useStreamForLobBinding() { + return sessionFactory.getFastSessionServices().useStreamForLobBinding(); + } + + @Override + public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) { + return sessionFactory.getFastSessionServices().remapSqlTypeDescriptor( sqlTypeDescriptor ); + } + + @Override + public TimeZone getJdbcTimeZone() { + return sessionFactory.getSessionFactoryOptions().getJdbcTimeZone(); + } + } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // for tests, for now public String getSql() { + if ( session != null ) { + session.cleanup(); + session = null; + } return sqlBuffer.toString(); } @@ -152,16 +225,16 @@ public abstract class AbstractSqlAstWalker @SuppressWarnings("unused") protected SqlAppender getSqlAppender() { - return sqlAppender; + return this; } - @SuppressWarnings("WeakerAccess") - protected void appendSql(String fragment) { + @Override + public void appendSql(String fragment) { sqlBuffer.append( fragment ); } - @SuppressWarnings("WeakerAccess") - protected void appendSql(char fragment) { + @Override + public void appendSql(char fragment) { sqlBuffer.append( fragment ); } @@ -187,21 +260,20 @@ public abstract class AbstractSqlAstWalker if ( !querySpec.isRoot() ) { appendSql( " (" ); } - visitSelectClause( querySpec.getSelectClause() ); + visitFromClause( querySpec.getFromClause() ); + visitWhereClause( querySpec ); + visitGroupByClause( querySpec ); + visitHavingClause( querySpec ); + visitOrderBy( querySpec ); + visitLimitOffsetClause( querySpec ); - FromClause fromClause = querySpec.getFromClause(); - if ( fromClause == null || fromClause.getRoots().isEmpty() ) { - String fromDual = getDialect().getFromDual(); - if ( !fromDual.isEmpty() ) { - appendSql( " " ); - appendSql( fromDual ); - } - } - else { - visitFromClause( fromClause ); + if ( !querySpec.isRoot() ) { + appendSql( ")" ); } + } + protected final void visitWhereClause(QuerySpec querySpec) { final Predicate whereClauseRestrictions = querySpec.getWhereClauseRestrictions(); if ( whereClauseRestrictions != null && !whereClauseRestrictions.isEmpty() ) { appendSql( " where " ); @@ -214,23 +286,152 @@ public abstract class AbstractSqlAstWalker clauseStack.pop(); } } + } + protected final void visitGroupByClause(QuerySpec querySpec) { + List groupByClauseExpressions = querySpec.getGroupByClauseExpressions(); + if ( !groupByClauseExpressions.isEmpty() ) { + appendSql( " group by " ); + + clauseStack.push( Clause.GROUP ); + String separator = NO_SEPARATOR; + try { + for ( Expression groupByClauseExpression : groupByClauseExpressions ) { + appendSql( separator ); + groupByClauseExpression.accept( this ); + separator = COMA_SEPARATOR; + } + } + finally { + clauseStack.pop(); + } + } + } + + protected final void visitHavingClause(QuerySpec querySpec) { + final Predicate havingClauseRestrictions = querySpec.getHavingClauseRestrictions(); + if ( havingClauseRestrictions != null && !havingClauseRestrictions.isEmpty() ) { + appendSql( " having " ); + + clauseStack.push( Clause.HAVING ); + try { + havingClauseRestrictions.accept( this ); + } + finally { + clauseStack.pop(); + } + } + } + + protected final void visitOrderBy(QuerySpec querySpec) { final List sortSpecifications = querySpec.getSortSpecifications(); if ( sortSpecifications != null && !sortSpecifications.isEmpty() ) { appendSql( " order by " ); - String separator = NO_SEPARATOR; - for ( SortSpecification sortSpecification : sortSpecifications ) { - appendSql( separator ); - visitSortSpecification( sortSpecification ); - separator = COMA_SEPARATOR; + clauseStack.push( Clause.ORDER ); + try { + String separator = NO_SEPARATOR; + for ( SortSpecification sortSpecification : sortSpecifications ) { + appendSql( separator ); + visitSortSpecification( sortSpecification ); + separator = COMA_SEPARATOR; + } + } + finally { + clauseStack.pop(); } } + } - visitLimitOffsetClause( querySpec ); + protected void emulateTupleComparison(final List lhsExpressions, final List rhsExpressions, ComparisonOperator operator) { + String separator = NO_SEPARATOR; - if ( !querySpec.isRoot() ) { - appendSql( ")" ); + final boolean isCurrentWhereClause = clauseStack.getCurrent() == Clause.WHERE; + if ( isCurrentWhereClause ) { + appendSql( OPEN_PARENTHESIS ); + } + + final int size = lhsExpressions.size(); + final String operatorText = operator.sqlText(); + assert size == rhsExpressions.size(); + + switch ( operator ) { + case EQUAL: + case NOT_EQUAL: + for ( int i = 0; i < size; i++ ) { + appendSql( separator ); + lhsExpressions.get( i ).accept( this ); + appendSql( operatorText ); + rhsExpressions.get( i ).accept( this ); + separator = " and "; + } + break; + case LESS_THAN_OR_EQUAL: + case GREATER_THAN_OR_EQUAL: + // Render (a, b) <= (1, 2) as: (a = 1 and b = 2) or (a < 1 or a = 1 and b < 2) + appendSql( OPEN_PARENTHESIS ); + for ( int i = 0; i < size; i++ ) { + appendSql( separator ); + lhsExpressions.get( i ).accept( this ); + appendSql( operatorText ); + rhsExpressions.get( i ).accept( this ); + separator = " and "; + } + appendSql( CLOSE_PARENTHESIS ); + appendSql( " or " ); + separator = NO_SEPARATOR; + case LESS_THAN: + case GREATER_THAN: + // Render (a, b) < (1, 2) as: (a < 1 or a = 1 and b < 2) + appendSql( OPEN_PARENTHESIS ); + for ( int i = 0; i < size; i++ ) { + int j = 0; + // Render the equals parts + for ( ; j < i; j++ ) { + appendSql( separator ); + lhsExpressions.get( i ).accept( this ); + appendSql( '=' ); + rhsExpressions.get( i ).accept( this ); + separator = " and "; + } + // Render the actual operator part for the current component + appendSql( separator ); + lhsExpressions.get( i ).accept( this ); + appendSql( operatorText ); + rhsExpressions.get( i ).accept( this ); + separator = " or "; + } + appendSql( CLOSE_PARENTHESIS ); + break; + } + + if ( isCurrentWhereClause ) { + appendSql( CLOSE_PARENTHESIS ); + } + } + + protected void renderSelectTupleComparison(final List lhsExpressions, SqlTuple tuple, ComparisonOperator operator) { + if ( dialect.supportsRowValueConstructorSyntax() ) { + appendSql( OPEN_PARENTHESIS ); + String separator = NO_SEPARATOR; + for ( SqlSelection lhsExpression : lhsExpressions ) { + appendSql( separator ); + lhsExpression.getExpression().accept( this ); + separator = COMA_SEPARATOR; + } + appendSql( CLOSE_PARENTHESIS ); + appendSql( " " ); + appendSql( operator.sqlText() ); + appendSql( " " ); + tuple.accept( this ); + } + else { + final List lhs = new ArrayList<>( lhsExpressions.size() ); + for ( SqlSelection lhsExpression : lhsExpressions ) { + lhs.add( lhsExpression.getExpression() ); + } + + emulateTupleComparison( lhs, tuple.getExpressions(), operator ); } } @@ -258,12 +459,6 @@ public abstract class AbstractSqlAstWalker sortSpecification.getSortExpression().accept( this ); - final String collation = sortSpecification.getCollation(); - if ( collation != null ) { - appendSql( " collate " ); - appendSql( collation ); - } - final SortOrder sortOrder = sortSpecification.getSortOrder(); if ( sortOrder == SortOrder.ASCENDING ) { appendSql( " asc" ); @@ -284,25 +479,25 @@ public abstract class AbstractSqlAstWalker @Override public void visitLimitOffsetClause(QuerySpec querySpec) { if ( querySpec.getOffsetClauseExpression() != null ) { - renderOffset( querySpec ); + renderOffset( querySpec.getOffsetClauseExpression() ); } if ( querySpec.getLimitClauseExpression() != null ) { - renderLimit( querySpec ); + renderLimit( querySpec.getLimitClauseExpression() ); } } @SuppressWarnings("WeakerAccess") - protected void renderOffset(QuerySpec querySpec) { + protected void renderOffset(Expression offsetExpression) { appendSql( " offset " ); - querySpec.getOffsetClauseExpression().accept( this ); + offsetExpression.accept( this ); appendSql( " rows" ); } @SuppressWarnings("WeakerAccess") - protected void renderLimit(QuerySpec querySpec) { + protected void renderLimit(Expression limitExpression) { appendSql( " fetch first " ); - querySpec.getLimitClauseExpression().accept( this ); + limitExpression.accept( this ); appendSql( " rows only" ); } @@ -343,13 +538,22 @@ public abstract class AbstractSqlAstWalker @Override public void visitFromClause(FromClause fromClause) { - appendSql( " from " ); + if ( fromClause == null || fromClause.getRoots().isEmpty() ) { + String fromDual = getDialect().getFromDual(); + if ( !fromDual.isEmpty() ) { + appendSql( " " ); + appendSql( fromDual ); + } + } + else { + appendSql( " from " ); - String separator = NO_SEPARATOR; - for ( TableGroup root : fromClause.getRoots() ) { - appendSql( separator ); - renderTableGroup( root ); - separator = COMA_SEPARATOR; + String separator = NO_SEPARATOR; + for ( TableGroup root : fromClause.getRoots() ) { + appendSql( separator ); + renderTableGroup( root ); + separator = COMA_SEPARATOR; + } } } @@ -399,12 +603,12 @@ public abstract class AbstractSqlAstWalker @SuppressWarnings("WeakerAccess") protected void renderTableReference(TableReference tableReference) { - sqlAppender.appendSql( tableReference.getTableExpression() ); + appendSql( tableReference.getTableExpression() ); final String identificationVariable = tableReference.getIdentificationVariable(); if ( identificationVariable != null ) { - sqlAppender.appendSql( getDialect().getTableAliasSeparator() ); - sqlAppender.appendSql( identificationVariable ); + appendSql( getDialect().getTableAliasSeparator() ); + appendSql( identificationVariable ); } } @@ -416,14 +620,14 @@ public abstract class AbstractSqlAstWalker } for ( TableReferenceJoin tableJoin : joins ) { - sqlAppender.appendSql( EMPTY_STRING ); - sqlAppender.appendSql( tableJoin.getJoinType().getText() ); - sqlAppender.appendSql( " join " ); + appendSql( EMPTY_STRING ); + appendSql( tableJoin.getJoinType().getText() ); + appendSql( " join " ); renderTableReference( tableJoin.getJoinedTableReference() ); if ( tableJoin.getJoinPredicate() != null && !tableJoin.getJoinPredicate().isEmpty() ) { - sqlAppender.appendSql( " on " ); + appendSql( " on " ); tableJoin.getJoinPredicate().accept( this ); } } @@ -590,6 +794,12 @@ public abstract class AbstractSqlAstWalker } } + @Override + public void visitCollate(Collate collate) { + collate.getExpression().accept( this ); + dialect.appendCollate( this, collate.getCollation() ); + } + @Override public void visitSqlSelectionExpression(SqlSelectionExpression expression) { final boolean useSelectionPosition = dialect.replaceResultVariableInOrderByClauseWithPosition(); @@ -907,7 +1117,7 @@ public abstract class AbstractSqlAstWalker @Override public void visitDuration(Duration duration) { duration.getMagnitude().accept( this ); - sqlAppender.appendSql( + appendSql( duration.getUnit().conversionFactor( NANOSECOND, getDialect() ) ); } @@ -915,25 +1125,13 @@ public abstract class AbstractSqlAstWalker @Override public void visitConversion(Conversion conversion) { conversion.getDuration().getMagnitude().accept( this ); - sqlAppender.appendSql( + appendSql( conversion.getDuration().getUnit().conversionFactor( conversion.getUnit(), getDialect() ) ); } - @Override - public void visitMemberOfPredicate(MemberOfPredicate memberOfPredicate) { - memberOfPredicate.getLeftHandExpression().accept( this ); - if ( memberOfPredicate.isNegated() ) { - appendSql( " not " ); - } - appendSql( " in (" ); - - visitQuerySpec( memberOfPredicate.getQuerySpec() ); - appendSql( ")" ); - } - @Override public void visitCaseSearchedExpression(CaseSearchedExpression caseSearchedExpression) { dialect.getCaseExpressionWalker().visitCaseSearchedExpression( caseSearchedExpression, sqlBuffer, this ); @@ -966,72 +1164,18 @@ public abstract class AbstractSqlAstWalker every.getSubquery().accept( this ); } - // @Override -// public void visitGenericParameter(GenericParameter parameter) { -// visitJdbcParameterBinder( parameter.getParameterBinder() ); -// -// if ( parameter instanceof JdbcParameter ) { -// jdbcParameters.addParameter( (JdbcParameter) parameter ); -// } -// } - - protected void visitJdbcParameterBinder(JdbcParameterBinder jdbcParameterBinder) { - parameterBinders.add( jdbcParameterBinder ); - - // todo (6.0) : ? wrap in cast function call if the literal occurs in SELECT (?based on Dialect?) - - appendSql( "?" ); - } - -// @Override -// public void visitNamedParameter(NamedParameter namedParameter) { -// visitJdbcParameterBinder( namedParameter ); -// } -// -// @Override -// public void visitPositionalParameter(PositionalParameter positionalParameter) { -// visitJdbcParameterBinder( positionalParameter ); -// } - - @Override public void visitJdbcLiteral(JdbcLiteral jdbcLiteral) { - renderAsLiteral( jdbcLiteral ); + visitLiteral( jdbcLiteral ); } @Override public void visitQueryLiteral(QueryLiteral queryLiteral) { - final QueryLiteralRendering queryLiteralRendering = getSessionFactory().getSessionFactoryOptions().getQueryLiteralRenderingMode(); - - switch( queryLiteralRendering ) { - case AS_LITERAL: { - renderAsLiteral( queryLiteral ); - break; - } - case AS_PARAM: { - visitJdbcParameterBinder( queryLiteral ); - break; - } - case AUTO: - case AS_PARAM_OUTSIDE_SELECT: { - if ( clauseStack.getCurrent() == Clause.SELECT ) { - renderAsLiteral( queryLiteral ); - } - else { - visitJdbcParameterBinder( queryLiteral ); - } - break; - } - default: { - throw new IllegalArgumentException( - "Unrecognized QueryLiteralRendering : " + queryLiteralRendering - ); - } - } + visitLiteral( queryLiteral ); } @SuppressWarnings("unchecked") - private void renderAsLiteral(Literal literal) { + private void visitLiteral(Literal literal) { if ( literal.getLiteralValue() == null ) { // todo : not sure we allow this "higher up" appendSql( SqlAppender.NULL_KEYWORD ); @@ -1040,13 +1184,25 @@ public abstract class AbstractSqlAstWalker assert literal.getExpressionType().getJdbcTypeCount( getTypeConfiguration() ) == 1; final JdbcMapping jdbcMapping = literal.getJdbcMapping(); final JdbcLiteralFormatter literalFormatter = jdbcMapping.getSqlTypeDescriptor().getJdbcLiteralFormatter( jdbcMapping.getJavaTypeDescriptor() ); - appendSql( - literalFormatter.toJdbcLiteral( - literal.getLiteralValue(), - dialect, - null - ) - ); + if ( literalFormatter == null ) { + parameterBinders.add( literal ); + + if ( clauseStack.getCurrent() == Clause.SELECT && dialect.requiresCastingOfParametersInSelectClause() ) { + castFunction().render( this, Collections.singletonList( new LiteralAsParameter<>( literal ) ), this ); + } + else { + parameterBinders.add( literal ); + } + } + else { + appendSql( + literalFormatter.toJdbcLiteral( + literal.getLiteralValue(), + dialect, + getSession() + ) + ); + } } } @@ -1069,7 +1225,7 @@ public abstract class AbstractSqlAstWalker @Override public void visitSelfRenderingExpression(SelfRenderingExpression expression) { - expression.renderToSql( sqlAppender, this, getSessionFactory() ); + expression.renderToSql( this, this, getSessionFactory() ); } // @Override @@ -1118,33 +1274,167 @@ public abstract class AbstractSqlAstWalker @Override public void visitInListPredicate(InListPredicate inListPredicate) { - inListPredicate.getTestExpression().accept( this ); - if ( inListPredicate.isNegated() ) { - appendSql( " not" ); - } - appendSql( " in (" ); - if ( inListPredicate.getListExpressions().isEmpty() ) { - appendSql( NULL_KEYWORD ); - } - else { + final SqlTuple lhsTuple; + if ( ( lhsTuple = getTuple( inListPredicate.getTestExpression() ) ) != null && !dialect.supportsRowValueConstructorSyntaxInInList() ) { + final ComparisonOperator comparisonOperator = inListPredicate.isNegated() ? ComparisonOperator.NOT_EQUAL : ComparisonOperator.EQUAL; String separator = NO_SEPARATOR; for ( Expression expression : inListPredicate.getListExpressions() ) { appendSql( separator ); - expression.accept( this ); - separator = COMA_SEPARATOR; + emulateTupleComparison( lhsTuple.getExpressions(), getTuple( expression ).getExpressions(), comparisonOperator ); + separator = " or "; } } - appendSql( CLOSE_PARENTHESIS ); + else { + inListPredicate.getTestExpression().accept( this ); + if ( inListPredicate.isNegated() ) { + appendSql( " not" ); + } + appendSql( " in (" ); + if ( inListPredicate.getListExpressions().isEmpty() ) { + appendSql( NULL_KEYWORD ); + } + else { + String separator = NO_SEPARATOR; + for ( Expression expression : inListPredicate.getListExpressions() ) { + appendSql( separator ); + expression.accept( this ); + separator = COMA_SEPARATOR; + } + } + appendSql( CLOSE_PARENTHESIS ); + } + } + + protected final SqlTuple getTuple(Expression expression) { + if ( expression instanceof SqlTuple ) { + return (SqlTuple) expression; + } + else if ( expression instanceof SqmParameterInterpretation ) { + final Expression resolvedExpression = ( (SqmParameterInterpretation) expression ).getResolvedExpression(); + if ( resolvedExpression instanceof SqlTuple ) { + return (SqlTuple) resolvedExpression; + } + } + else if ( expression instanceof EmbeddableValuedPathInterpretation ) { + return ( (EmbeddableValuedPathInterpretation) expression ).getSqlExpression(); + } + else if ( expression instanceof NonAggregatedCompositeValuedPathInterpretation ) { + return ( (NonAggregatedCompositeValuedPathInterpretation) expression ).getSqlExpression(); + } + return null; } @Override public void visitInSubQueryPredicate(InSubQueryPredicate inSubQueryPredicate) { - inSubQueryPredicate.getTestExpression().accept( this ); - if ( inSubQueryPredicate.isNegated() ) { - appendSql( " not" ); + final SqlTuple lhsTuple; + if ( ( lhsTuple = getTuple( inSubQueryPredicate.getTestExpression() ) ) != null && !dialect.supportsRowValueConstructorSyntaxInInList() ) { + emulateTupleSubQueryPredicate( + inSubQueryPredicate, + inSubQueryPredicate.isNegated(), + inSubQueryPredicate.getSubQuery(), + lhsTuple, + ComparisonOperator.EQUAL + ); + } + else { + inSubQueryPredicate.getTestExpression().accept( this ); + if ( inSubQueryPredicate.isNegated() ) { + appendSql( " not" ); + } + appendSql( " in " ); + visitQuerySpec( inSubQueryPredicate.getSubQuery() ); + } + } + + protected void emulateTupleSubQueryPredicate( + Predicate predicate, + boolean negated, + QuerySpec subQuery, + SqlTuple lhsTuple, + ComparisonOperator tupleComparisonOperator) { + if ( subQuery.getLimitClauseExpression() == null && subQuery.getOffsetClauseExpression() == null ) { + // We can only emulate the tuple sub query predicate as exists predicate when there are no limit/offsets + if ( negated ) { + appendSql( "not " ); + } + appendSql( "exists (select 1" ); + visitFromClause( subQuery.getFromClause() ); + + appendSql( " where " ); + + // TODO: use HAVING clause if it has a group by + clauseStack.push( Clause.WHERE ); + try { + renderSelectTupleComparison( + subQuery.getSelectClause().getSqlSelections(), + lhsTuple, + tupleComparisonOperator + ); + appendSql( " and (" ); + final Predicate whereClauseRestrictions = subQuery.getWhereClauseRestrictions(); + if ( whereClauseRestrictions != null ) { + whereClauseRestrictions.accept( this ); + } + appendSql( ')' ); + } + finally { + clauseStack.pop(); + } + + appendSql( ")" ); + } + else { + // TODO: We could use nested queries and use row numbers to emulate this + throw new IllegalArgumentException( "Can't emulate in predicate with tuples and limit/offset: " + predicate ); + } + } + + /** + * An optimized emulation for relational tuple subquery comparisons. + * The idea of this method is to use limit 1 to select the max or min tuple and only compare against that. + */ + protected void emulateQuantifiedTupleSubQueryPredicate( + Predicate predicate, + QuerySpec subQuery, + SqlTuple lhsTuple, + ComparisonOperator tupleComparisonOperator) { + if ( subQuery.getLimitClauseExpression() == null && subQuery.getOffsetClauseExpression() == null ) { + // We can only emulate the tuple sub query predicate as exists predicate when there are no limit/offsets + lhsTuple.accept( this ); + appendSql( " " ); + appendSql( tupleComparisonOperator.sqlText() ); + appendSql( " " ); + + appendSql( "(" ); + visitSelectClause( subQuery.getSelectClause() ); + visitFromClause( subQuery.getFromClause() ); + visitWhereClause( subQuery ); + + appendSql( " order by " ); + boolean asc = tupleComparisonOperator == ComparisonOperator.LESS_THAN || tupleComparisonOperator == ComparisonOperator.LESS_THAN_OR_EQUAL; + final List sqlSelections = subQuery.getSelectClause().getSqlSelections(); + final String order; + if ( tupleComparisonOperator == ComparisonOperator.LESS_THAN || tupleComparisonOperator == ComparisonOperator.LESS_THAN_OR_EQUAL ) { + // Default order is asc so we don't need to specify the order explicitly + order = ""; + } + else { + order = " desc"; + } + appendSql( "1" ); + appendSql( order ); + for ( int i = 1; i < sqlSelections.size(); i++ ) { + appendSql( COMA_SEPARATOR ); + appendSql( Integer.toString( i + 1 ) ); + appendSql( order ); + } + renderLimit( ONE_LITERAL ); + appendSql( ")" ); + } + else { + // TODO: We could use nested queries and use row numbers to emulate this + throw new IllegalArgumentException( "Can't emulate in predicate with tuples and limit/offset: " + predicate ); } - appendSql( " in " ); - visitQuerySpec( inSubQueryPredicate.getSubQuery() ); } @Override @@ -1199,19 +1489,19 @@ public abstract class AbstractSqlAstWalker @Override public void visitNullnessPredicate(NullnessPredicate nullnessPredicate) { final Expression expression = nullnessPredicate.getExpression(); + final String predicateValue; + if ( nullnessPredicate.isNegated() ) { + predicateValue = " is not null"; + } + else { + predicateValue = " is null"; + } if ( expression instanceof EmbeddableValuedPathInterpretation ) { final EmbeddableValuedPathInterpretation embeddableValuedPathInterpretation = (EmbeddableValuedPathInterpretation) expression; final Expression sqlExpression = embeddableValuedPathInterpretation.getSqlExpression(); - String predicateValue; - if ( nullnessPredicate.isNegated() ) { - predicateValue = " is not null"; - } - else { - predicateValue = " is null"; - } - if ( sqlExpression instanceof SqlTuple ) { - SqlTuple tuple = (SqlTuple) sqlExpression; + final SqlTuple tuple; + if ( ( tuple = getTuple( sqlExpression ) ) != null ) { String separator = NO_SEPARATOR; boolean isCurrentWhereClause = clauseStack.getCurrent() == Clause.WHERE; @@ -1232,22 +1522,12 @@ public abstract class AbstractSqlAstWalker } else { expression.accept( this ); - if ( nullnessPredicate.isNegated() ) { - appendSql( " is not null" ); - } - else { - appendSql( " is null" ); - } + appendSql( predicateValue ); } } else { expression.accept( this ); - if ( nullnessPredicate.isNegated() ) { - appendSql( " is not null" ); - } - else { - appendSql( " is null" ); - } + appendSql( predicateValue ); } } @@ -1267,11 +1547,105 @@ public abstract class AbstractSqlAstWalker // // transform this into a // } // - comparisonPredicate.getLeftHandExpression().accept( this ); - appendSql( " " ); - appendSql( comparisonPredicate.getOperator().sqlText() ); - appendSql( " " ); - comparisonPredicate.getRightHandExpression().accept( this ); + final SqlTuple lhsTuple; + final SqlTuple rhsTuple; + if ( ( lhsTuple = getTuple( comparisonPredicate.getLeftHandExpression() ) ) != null ) { + final Expression rhsExpression = comparisonPredicate.getRightHandExpression(); + final boolean all; + final QuerySpec subquery; + + // Handle emulation of quantified comparison + if ( rhsExpression instanceof QuerySpec ) { + subquery = (QuerySpec) rhsExpression; + all = true; + } + else if ( rhsExpression instanceof Every ) { + subquery = ( (Every) rhsExpression ).getSubquery(); + all = true; + } + else if ( rhsExpression instanceof Any ) { + subquery = ( (Any) rhsExpression ).getSubquery(); + all = false; + } + else { + subquery = null; + all = false; + } + + final ComparisonOperator operator = comparisonPredicate.getOperator(); + if ( subquery != null && !dialect.supportsRowValueConstructorSyntaxInQuantifiedPredicates() ) { + // For quantified relational comparisons, we can do an optimized emulation + if ( all && operator != ComparisonOperator.EQUAL && operator != ComparisonOperator.NOT_EQUAL && dialect.supportsRowValueConstructorSyntax() ) { + emulateQuantifiedTupleSubQueryPredicate( + comparisonPredicate, + subquery, + lhsTuple, + operator + ); + } + else { + emulateTupleSubQueryPredicate( + comparisonPredicate, + all, + subquery, + lhsTuple, + all ? operator.negated() : operator + ); + } + } + else if ( !dialect.supportsRowValueConstructorSyntax() ) { + rhsTuple = getTuple( rhsExpression ); + assert rhsTuple != null; + emulateTupleComparison( + lhsTuple.getExpressions(), + rhsTuple.getExpressions(), + operator + ); + } + else { + comparisonPredicate.getLeftHandExpression().accept( this ); + appendSql( " " ); + appendSql( operator.sqlText() ); + appendSql( " " ); + rhsExpression.accept( this ); + } + } + else if ( ( rhsTuple = getTuple( comparisonPredicate.getRightHandExpression() ) ) != null ) { + final Expression lhsExpression = comparisonPredicate.getLeftHandExpression(); + + if ( lhsExpression instanceof QuerySpec ) { + final QuerySpec subquery = (QuerySpec) lhsExpression; + + if ( dialect.supportsRowValueConstructorSyntax() ) { + lhsExpression.accept( this ); + appendSql( " " ); + appendSql( comparisonPredicate.getOperator().sqlText() ); + appendSql( " " ); + comparisonPredicate.getRightHandExpression().accept( this ); + } + else { + emulateTupleSubQueryPredicate( + comparisonPredicate, + false, + subquery, + rhsTuple, + // Since we switch the order of operands, we have to invert the operator + comparisonPredicate.getOperator().invert() + ); + } + } + else { + throw new IllegalStateException( + "Unsupported tuple comparison combination. LHS is neither a tuple nor a tuple subquery but RHS is a tuple: " + comparisonPredicate ); + } + } + else { + comparisonPredicate.getLeftHandExpression().accept( this ); + appendSql( " " ); + appendSql( comparisonPredicate.getOperator().sqlText() ); + appendSql( " " ); + comparisonPredicate.getRightHandExpression().accept( this ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAppender.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAppender.java index 93a64ead7f..bd2a6dcdd1 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAppender.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAppender.java @@ -27,4 +27,18 @@ public interface SqlAppender { * Add the passed fragment into the in-flight buffer */ void appendSql(String fragment); + + void appendSql(char fragment); + + default void appendQuoted(String value, char quoteChar) { + appendSql( quoteChar ); + for ( int i = 0; i < value.length(); i++ ) { + final char c = value.charAt( i ); + if ( c == quoteChar ) { + appendSql( quoteChar ); + } + appendSql( c ); + } + appendSql( quoteChar ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlSelection.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlSelection.java index a051969b20..82237cbe67 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlSelection.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlSelection.java @@ -8,6 +8,7 @@ package org.hibernate.sql.ast.spi; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.MappingModelExpressable; +import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata; import org.hibernate.sql.results.jdbc.spi.RowProcessingState; import org.hibernate.sql.ast.SqlAstWalker; @@ -46,6 +47,11 @@ public interface SqlSelection { return getValuesArrayPosition() + 1; } + /** + * The underlying expression. + */ + Expression getExpression(); + /** * Get the type of the expression */ diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Collate.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Collate.java new file mode 100644 index 0000000000..0caf4d8b7a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Collate.java @@ -0,0 +1,58 @@ +/* + * 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.tree.expression; + +import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.metamodel.mapping.MappingModelExpressable; +import org.hibernate.metamodel.mapping.SqlExpressable; +import org.hibernate.sql.ast.SqlAstWalker; +import org.hibernate.sql.ast.tree.SqlAstNode; + +/** + * @author Christian Beikov + */ +public class Collate implements Expression, SqlExpressable, SqlAstNode { + + private final Expression expression; + private final String collation; + + public Collate(Expression expression, String collation) { + this.expression = expression; + this.collation = collation; + } + + public Expression getExpression() { + return expression; + } + + public String getCollation() { + return collation; + } + + @Override + public JdbcMapping getJdbcMapping() { + if ( expression instanceof SqlExpressable ) { + return ( (SqlExpressable) expression ).getJdbcMapping(); + } + + if ( getExpressionType() instanceof SqlExpressable ) { + return ( (SqlExpressable) getExpressionType() ).getJdbcMapping(); + } + + return null; + } + + @Override + public MappingModelExpressable getExpressionType() { + return expression.getExpressionType(); + } + + @Override + public void accept(SqlAstWalker sqlTreeWalker) { + sqlTreeWalker.visitCollate( this ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/LiteralAsParameter.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/LiteralAsParameter.java new file mode 100644 index 0000000000..a6a161984c --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/LiteralAsParameter.java @@ -0,0 +1,42 @@ +/* + * 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.tree.expression; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.MappingModelExpressable; +import org.hibernate.sql.ast.SqlAstWalker; +import org.hibernate.sql.ast.spi.SqlAppender; + +/** + * A wrapper for a literal to render as parameter through a cast function. + * + * @see org.hibernate.sql.ast.spi.AbstractSqlAstWalker + * + * @author Christian beikov + */ +public class LiteralAsParameter implements SelfRenderingExpression { + + private final Literal literal; + + public LiteralAsParameter(Literal literal) { + this.literal = literal; + } + + @Override + public void renderToSql(SqlAppender sqlAppender, SqlAstWalker walker, SessionFactoryImplementor sessionFactory) { + sqlAppender.appendSql( "?" ); + } + + @Override + public MappingModelExpressable getExpressionType() { + return literal.getExpressionType(); + } + + public Literal getLiteral() { + return literal; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/predicate/MemberOfPredicate.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/predicate/MemberOfPredicate.java deleted file mode 100644 index 1af8a8e6cf..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/predicate/MemberOfPredicate.java +++ /dev/null @@ -1,51 +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.tree.predicate; - -import org.hibernate.sql.ast.SqlAstWalker; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.sql.ast.tree.select.QuerySpec; - -/** - * @author Andrea Boriero - */ -public class MemberOfPredicate implements Predicate { - private final Expression leftHandExpression; - private final QuerySpec querySpec; - private final boolean isNegated; - - public MemberOfPredicate( - Expression leftHandExpression, - boolean isNegated, - QuerySpec querySpec) { - this.leftHandExpression = leftHandExpression; - this.isNegated = isNegated; - this.querySpec = querySpec; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public void accept(SqlAstWalker sqlTreeWalker) { - sqlTreeWalker.visitMemberOfPredicate( this ); - } - - public boolean isNegated() { - return isNegated; - } - - public Expression getLeftHandExpression() { - return leftHandExpression; - } - - public QuerySpec getQuerySpec() { - return querySpec; - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QuerySpec.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QuerySpec.java index 1760a3ab6e..9505ef656c 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QuerySpec.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QuerySpec.java @@ -7,6 +7,7 @@ package org.hibernate.sql.ast.tree.select; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.function.Consumer; @@ -38,6 +39,10 @@ public class QuerySpec implements SqlAstNode, PredicateContainer, Expression, Ct private final SelectClause selectClause = new SelectClause(); private Predicate whereClauseRestrictions; + + private List groupByClauseExpressions = Collections.emptyList(); + private Predicate havingClauseRestrictions; + private List sortSpecifications; private Expression limitClauseExpression; private Expression offsetClauseExpression; @@ -77,6 +82,22 @@ public class QuerySpec implements SqlAstNode, PredicateContainer, Expression, Ct this.whereClauseRestrictions = SqlAstTreeHelper.combinePredicates( this.whereClauseRestrictions, predicate ); } + public List getGroupByClauseExpressions() { + return groupByClauseExpressions; + } + + public void setGroupByClauseExpressions(List groupByClauseExpressions) { + this.groupByClauseExpressions = groupByClauseExpressions == null ? Collections.emptyList() : groupByClauseExpressions; + } + + public Predicate getHavingClauseRestrictions() { + return havingClauseRestrictions; + } + + public void setHavingClauseRestrictions(Predicate havingClauseRestrictions) { + this.havingClauseRestrictions = havingClauseRestrictions; + } + public List getSortSpecifications() { return sortSpecifications; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/SortSpecification.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/SortSpecification.java index 606bf46d54..6502d49419 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/SortSpecification.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/SortSpecification.java @@ -10,6 +10,7 @@ import org.hibernate.NullPrecedence; import org.hibernate.SortOrder; import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Collate; import org.hibernate.sql.ast.tree.expression.Expression; /** @@ -17,7 +18,6 @@ import org.hibernate.sql.ast.tree.expression.Expression; */ public class SortSpecification implements SqlAstNode { private final Expression sortExpression; - private final String collation; private final SortOrder sortOrder; private final NullPrecedence nullPrecedence; @@ -26,8 +26,12 @@ public class SortSpecification implements SqlAstNode { } public SortSpecification(Expression sortExpression, String collation, SortOrder sortOrder, NullPrecedence nullPrecedence) { - this.sortExpression = sortExpression; - this.collation = collation; + if ( collation == null ) { + this.sortExpression = sortExpression; + } + else { + this.sortExpression = new Collate( sortExpression, collation ); + } this.sortOrder = sortOrder; this.nullPrecedence = nullPrecedence; } @@ -36,10 +40,6 @@ public class SortSpecification implements SqlAstNode { return sortExpression; } - public String getCollation() { - return collation; - } - public SortOrder getSortOrder() { return sortOrder; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/SqlSelectionImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/SqlSelectionImpl.java index 4c0a28de59..cf9f126d2a 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/SqlSelectionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/SqlSelectionImpl.java @@ -46,7 +46,8 @@ public class SqlSelectionImpl implements SqlSelection, SqlExpressionAccess { this.sqlExpression = sqlExpression; } - public Expression getWrappedSqlExpression() { + @Override + public Expression getExpression() { return sqlExpression; } @@ -69,7 +70,7 @@ public class SqlSelectionImpl implements SqlSelection, SqlExpressionAccess { @Override public MappingModelExpressable getExpressionType() { - return getWrappedSqlExpression().getExpressionType(); + return getExpression().getExpressionType(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/DateTimeUtils.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/DateTimeUtils.java index 6d1e5357dd..4eee54aac8 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/DateTimeUtils.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/DateTimeUtils.java @@ -7,6 +7,8 @@ package org.hibernate.type.descriptor; import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.ChronoField; @@ -33,14 +35,18 @@ public final class DateTimeUtils { public static final String FORMAT_STRING_TIMESTAMP = "yyyy-MM-dd HH:mm:ss"; public static final String FORMAT_STRING_TIMESTAMP_WITH_MILLIS = FORMAT_STRING_TIMESTAMP + ".SSS"; public static final String FORMAT_STRING_TIMESTAMP_WITH_MICROS = FORMAT_STRING_TIMESTAMP + ".SSSSSS"; - public static final String FORMAT_STRING_TIMESTAMP_WITH_OFFSET = FORMAT_STRING_TIMESTAMP_WITH_MICROS + "xxx"; + public static final String FORMAT_STRING_TIMESTAMP_WITH_MILLIS_AND_OFFSET = FORMAT_STRING_TIMESTAMP_WITH_MILLIS + "xxx"; + public static final String FORMAT_STRING_TIMESTAMP_WITH_MICROS_AND_OFFSET = FORMAT_STRING_TIMESTAMP_WITH_MICROS + "xxx"; public static final DateTimeFormatter DATE_TIME_FORMATTER_DATE = DateTimeFormatter.ofPattern( FORMAT_STRING_DATE, Locale.ENGLISH ); public static final DateTimeFormatter DATE_TIME_FORMATTER_TIME_WITH_OFFSET = DateTimeFormatter.ofPattern( FORMAT_STRING_TIME_WITH_OFFSET, Locale.ENGLISH ); public static final DateTimeFormatter DATE_TIME_FORMATTER_TIME = DateTimeFormatter.ofPattern( FORMAT_STRING_TIME, Locale.ENGLISH ); public static final DateTimeFormatter DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS = DateTimeFormatter.ofPattern(FORMAT_STRING_TIMESTAMP_WITH_MILLIS, Locale.ENGLISH ); public static final DateTimeFormatter DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS = DateTimeFormatter.ofPattern(FORMAT_STRING_TIMESTAMP_WITH_MICROS, Locale.ENGLISH ); - public static final DateTimeFormatter DATE_TIME_FORMATTER_TIMESTAMP_WITH_OFFSET = DateTimeFormatter.ofPattern( FORMAT_STRING_TIMESTAMP_WITH_OFFSET, Locale.ENGLISH ); + public static final DateTimeFormatter DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS_AND_OFFSET = DateTimeFormatter.ofPattern( + FORMAT_STRING_TIMESTAMP_WITH_MILLIS_AND_OFFSET, Locale.ENGLISH ); + public static final DateTimeFormatter DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS_AND_OFFSET = DateTimeFormatter.ofPattern( + FORMAT_STRING_TIMESTAMP_WITH_MICROS_AND_OFFSET, Locale.ENGLISH ); public static final String JDBC_ESCAPE_START_DATE = "{d '"; public static final String JDBC_ESCAPE_START_TIME = "{t '"; @@ -65,6 +71,21 @@ public final class DateTimeUtils { .optionalStart().appendZoneOrOffsetId().optionalEnd() .toFormatter(); + private static final ThreadLocal LOCAL_DATE_FORMAT = ThreadLocal.withInitial( DateTimeUtils::simpleDateFormatDate ); + private static final ThreadLocal LOCAL_TIME_FORMAT = ThreadLocal.withInitial( DateTimeUtils::simpleDateFormatTime ); + private static final ThreadLocal TIMESTAMP_WITH_MILLIS_FORMAT = ThreadLocal.withInitial( + () -> new SimpleDateFormat( + FORMAT_STRING_TIMESTAMP_WITH_MILLIS, + Locale.ENGLISH + ) + ); + private static final ThreadLocal TIMESTAMP_WITH_MICROS_FORMAT = ThreadLocal.withInitial( + () -> new SimpleDateFormat( + FORMAT_STRING_TIMESTAMP_WITH_MICROS, + Locale.ENGLISH + ) + ); + /** * Pattern used for parsing literal offset datetimes in HQL. * @@ -82,35 +103,89 @@ public final class DateTimeUtils { .appendOffset("+HH:mm", "+00") .toFormatter(); - public static String formatAsTimestampWithMicros(TemporalAccessor temporalAccessor) { - return temporalAccessor.isSupported(ChronoField.OFFSET_SECONDS) - ? DATE_TIME_FORMATTER_TIMESTAMP_WITH_OFFSET.format( temporalAccessor ) - : DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS.format( temporalAccessor ); + public static String formatAsTimestampWithMicros(TemporalAccessor temporalAccessor, boolean supportsOffset, TimeZone jdbcTimeZone) { + if ( temporalAccessor.isSupported(ChronoField.OFFSET_SECONDS) ) { + if ( supportsOffset ) { + return DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS_AND_OFFSET.format( temporalAccessor ); + } + else { + return DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS.format( + LocalDateTime.ofInstant( + Instant.from( temporalAccessor ), + jdbcTimeZone.toZoneId() + ) + ); + } + } + else { + return DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS.format( temporalAccessor ); + } } - public static String formatAsTimestampWithMillis(TemporalAccessor temporalAccessor) { - return DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS.format( temporalAccessor ); + public static String formatAsTimestampWithMillis(TemporalAccessor temporalAccessor, boolean supportsOffset, TimeZone jdbcTimeZone) { + if ( temporalAccessor.isSupported(ChronoField.OFFSET_SECONDS) ) { + if ( supportsOffset ) { + return DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS_AND_OFFSET.format( temporalAccessor ); + } + else { + return DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS.format( + LocalDateTime.ofInstant( + Instant.from( temporalAccessor ), + jdbcTimeZone.toZoneId() + ) + ); + } + } + else { + return DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS.format( temporalAccessor ); + } } public static String formatAsDate(TemporalAccessor temporalAccessor) { return DATE_TIME_FORMATTER_DATE.format( temporalAccessor ); } - public static String formatAsTime(TemporalAccessor temporalAccessor) { + public static String formatAsTime(TemporalAccessor temporalAccessor, boolean supportsOffset, TimeZone jdbcTimeZone) { if ( temporalAccessor.isSupported(ChronoField.OFFSET_SECONDS) ) { - return DATE_TIME_FORMATTER_TIME_WITH_OFFSET.format(temporalAccessor); + if ( supportsOffset ) { + return DATE_TIME_FORMATTER_TIME_WITH_OFFSET.format( temporalAccessor ); + } + else { + return DATE_TIME_FORMATTER_TIME.format( + LocalDateTime.ofInstant( + Instant.from( temporalAccessor ), + jdbcTimeZone.toZoneId() + ) + ); + } } else { - return DATE_TIME_FORMATTER_TIME.format(temporalAccessor); + return DATE_TIME_FORMATTER_TIME.format( temporalAccessor ); } } - public static String formatAsTimestampWithMillis(java.util.Date date) { - return simpleDateFormatTimestampWithMillis().format( date ); + public static String formatAsTimestampWithMillis(java.util.Date date, TimeZone jdbcTimeZone) { + final SimpleDateFormat simpleDateFormat = TIMESTAMP_WITH_MILLIS_FORMAT.get(); + final TimeZone originalTimeZone = simpleDateFormat.getTimeZone(); + try { + simpleDateFormat.setTimeZone( jdbcTimeZone ); + return simpleDateFormat.format( date ); + } + finally { + simpleDateFormat.setTimeZone( originalTimeZone ); + } } - public static String formatAsTimestampWithMicros(java.util.Date date) { - return simpleDateFormatTimestampWithMicros().format( date ); + public static String formatAsTimestampWithMicros(java.util.Date date, TimeZone jdbcTimeZone) { + final SimpleDateFormat simpleDateFormat = TIMESTAMP_WITH_MICROS_FORMAT.get(); + final TimeZone originalTimeZone = simpleDateFormat.getTimeZone(); + try { + simpleDateFormat.setTimeZone( jdbcTimeZone ); + return simpleDateFormat.format( date ); + } + finally { + simpleDateFormat.setTimeZone( originalTimeZone ); + } } public static String wrapAsJdbcDateLiteral(String literal) { @@ -137,16 +212,8 @@ public final class DateTimeUtils { return "timestamp '" + literal + "'"; } - public static SimpleDateFormat simpleDateFormatTimestampWithMillis() { - return new SimpleDateFormat(FORMAT_STRING_TIMESTAMP_WITH_MILLIS, Locale.ENGLISH ); - } - - public static SimpleDateFormat simpleDateFormatTimestampWithMicros() { - return new SimpleDateFormat(FORMAT_STRING_TIMESTAMP_WITH_MICROS, Locale.ENGLISH ); - } - public static String formatAsDate(java.util.Date date) { - return simpleDateFormatDate().format( date ); + return LOCAL_DATE_FORMAT.get().format( date ); } public static SimpleDateFormat simpleDateFormatDate() { @@ -154,15 +221,23 @@ public final class DateTimeUtils { } public static String formatAsTime(java.util.Date date) { - return simpleDateFormatTime().format( date ); + return LOCAL_TIME_FORMAT.get().format( date ); } public static SimpleDateFormat simpleDateFormatTime() { return new SimpleDateFormat( FORMAT_STRING_TIME, Locale.ENGLISH ); } - public static String formatAsTimestampWithMillis(java.util.Calendar calendar) { - return simpleDateFormatTimestampWithMillis( calendar.getTimeZone() ).format( calendar.getTime() ); + public static String formatAsTimestampWithMillis(java.util.Calendar calendar, TimeZone jdbcTimeZone) { + final SimpleDateFormat simpleDateFormat = TIMESTAMP_WITH_MILLIS_FORMAT.get(); + final TimeZone originalTimeZone = simpleDateFormat.getTimeZone(); + try { + simpleDateFormat.setTimeZone( jdbcTimeZone ); + return simpleDateFormat.format( calendar.getTime() ); + } + finally { + simpleDateFormat.setTimeZone( originalTimeZone ); + } } public static SimpleDateFormat simpleDateFormatTimestampWithMillis(TimeZone timeZone) { @@ -171,8 +246,16 @@ public final class DateTimeUtils { return formatter; } - public static String formatAsTimestampWithMicros(java.util.Calendar calendar) { - return simpleDateFormatTimestampWithMicros( calendar.getTimeZone() ).format( calendar.getTime() ); + public static String formatAsTimestampWithMicros(java.util.Calendar calendar, TimeZone jdbcTimeZone) { + final SimpleDateFormat simpleDateFormat = TIMESTAMP_WITH_MICROS_FORMAT.get(); + final TimeZone originalTimeZone = simpleDateFormat.getTimeZone(); + try { + simpleDateFormat.setTimeZone( jdbcTimeZone ); + return simpleDateFormat.format( calendar.getTime() ); + } + finally { + simpleDateFormat.setTimeZone( originalTimeZone ); + } } public static SimpleDateFormat simpleDateFormatTimestampWithMicros(TimeZone timeZone) { @@ -182,7 +265,15 @@ public final class DateTimeUtils { } public static String formatAsDate(java.util.Calendar calendar) { - return simpleDateFormatDate( calendar.getTimeZone() ).format( calendar.getTime() ); + final SimpleDateFormat simpleDateFormat = LOCAL_DATE_FORMAT.get(); + final TimeZone originalTimeZone = simpleDateFormat.getTimeZone(); + try { + simpleDateFormat.setTimeZone( calendar.getTimeZone() ); + return simpleDateFormat.format( calendar.getTime() ); + } + finally { + simpleDateFormat.setTimeZone( originalTimeZone ); + } } public static SimpleDateFormat simpleDateFormatDate(TimeZone timeZone) { @@ -192,7 +283,15 @@ public final class DateTimeUtils { } public static String formatAsTime(java.util.Calendar calendar) { - return simpleDateFormatTime( calendar.getTimeZone() ).format( calendar.getTime() ); + final SimpleDateFormat simpleDateFormat = LOCAL_TIME_FORMAT.get(); + final TimeZone originalTimeZone = simpleDateFormat.getTimeZone(); + try { + simpleDateFormat.setTimeZone( calendar.getTimeZone() ); + return simpleDateFormat.format( calendar.getTime() ); + } + finally { + simpleDateFormat.setTimeZone( originalTimeZone ); + } } public static SimpleDateFormat simpleDateFormatTime(TimeZone timeZone) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/TimestampWithTimeZoneDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/TimestampWithTimeZoneDescriptor.java index 168f0402bf..1a5f6362c4 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/TimestampWithTimeZoneDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/TimestampWithTimeZoneDescriptor.java @@ -75,13 +75,13 @@ public class TimestampWithTimeZoneDescriptor implements SqlTypeDescriptor { int index, WrapperOptions wrapperOptions) throws SQLException { try { - final OffsetDateTime dateTime = javaTypeDescriptor.unwrap( value, OffsetDateTime.class, wrapperOptions.getSession() ); + final OffsetDateTime dateTime = javaTypeDescriptor.unwrap( value, OffsetDateTime.class, wrapperOptions ); // supposed to be supported in JDBC 4.2 st.setObject( index, dateTime, Types.TIMESTAMP_WITH_TIMEZONE ); } catch (SQLException|AbstractMethodError e) { // fall back to treating it as a JDBC Timestamp - final Timestamp timestamp = javaTypeDescriptor.unwrap( value, Timestamp.class, wrapperOptions.getSession() ); + final Timestamp timestamp = javaTypeDescriptor.unwrap( value, Timestamp.class, wrapperOptions ); st.setTimestamp( index, timestamp ); } } @@ -94,13 +94,13 @@ public class TimestampWithTimeZoneDescriptor implements SqlTypeDescriptor { WrapperOptions wrapperOptions) throws SQLException { try { - final OffsetDateTime dateTime = javaTypeDescriptor.unwrap( value, OffsetDateTime.class, wrapperOptions.getSession() ); + final OffsetDateTime dateTime = javaTypeDescriptor.unwrap( value, OffsetDateTime.class, wrapperOptions ); // supposed to be supported in JDBC 4.2 st.setObject( name, dateTime, Types.TIMESTAMP_WITH_TIMEZONE ); } catch (SQLException|AbstractMethodError e) { // fall back to treating it as a JDBC Timestamp - final Timestamp timestamp = javaTypeDescriptor.unwrap( value, Timestamp.class, wrapperOptions.getSession() ); + final Timestamp timestamp = javaTypeDescriptor.unwrap( value, Timestamp.class, wrapperOptions ); st.setTimestamp( name, timestamp ); } } @@ -114,11 +114,11 @@ public class TimestampWithTimeZoneDescriptor implements SqlTypeDescriptor { protected X doExtract(ResultSet rs, int position, WrapperOptions wrapperOptions) throws SQLException { try { // supposed to be supported in JDBC 4.2 - return javaTypeDescriptor.wrap( rs.getObject( position, OffsetDateTime.class ), wrapperOptions.getSession() ); + return javaTypeDescriptor.wrap( rs.getObject( position, OffsetDateTime.class ), wrapperOptions ); } catch (SQLException|AbstractMethodError e) { // fall back to treating it as a JDBC Timestamp - return javaTypeDescriptor.wrap( rs.getTimestamp( position ), wrapperOptions.getSession() ); + return javaTypeDescriptor.wrap( rs.getTimestamp( position ), wrapperOptions ); } } @@ -126,11 +126,11 @@ public class TimestampWithTimeZoneDescriptor implements SqlTypeDescriptor { protected X doExtract(CallableStatement statement, int position, WrapperOptions wrapperOptions) throws SQLException { try { // supposed to be supported in JDBC 4.2 - return javaTypeDescriptor.wrap( statement.getObject( position, OffsetDateTime.class ), wrapperOptions.getSession() ); + return javaTypeDescriptor.wrap( statement.getObject( position, OffsetDateTime.class ), wrapperOptions ); } catch (SQLException|AbstractMethodError e) { // fall back to treating it as a JDBC Timestamp - return javaTypeDescriptor.wrap( statement.getTimestamp( position ), wrapperOptions.getSession() ); + return javaTypeDescriptor.wrap( statement.getTimestamp( position ), wrapperOptions ); } } @@ -138,11 +138,11 @@ public class TimestampWithTimeZoneDescriptor implements SqlTypeDescriptor { protected X doExtract(CallableStatement statement, String name, WrapperOptions wrapperOptions) throws SQLException { try { // supposed to be supported in JDBC 4.2 - return javaTypeDescriptor.wrap( statement.getObject( name, OffsetDateTime.class ), wrapperOptions.getSession() ); + return javaTypeDescriptor.wrap( statement.getObject( name, OffsetDateTime.class ), wrapperOptions ); } catch (SQLException|AbstractMethodError e) { // fall back to treating it as a JDBC Timestamp - return javaTypeDescriptor.wrap( statement.getTimestamp( name ), wrapperOptions.getSession() ); + return javaTypeDescriptor.wrap( statement.getTimestamp( name ), wrapperOptions ); } } }; diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/internal/JdbcLiteralFormatterTemporal.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/internal/JdbcLiteralFormatterTemporal.java index a99baa6377..25d5b5d52b 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/internal/JdbcLiteralFormatterTemporal.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/internal/JdbcLiteralFormatterTemporal.java @@ -7,6 +7,7 @@ package org.hibernate.type.descriptor.sql.internal; import java.time.temporal.TemporalAccessor; +import java.util.TimeZone; import javax.persistence.TemporalType; import org.hibernate.dialect.Dialect; @@ -27,23 +28,33 @@ public class JdbcLiteralFormatterTemporal extends BasicJdbcLiteralFormatter { @Override public String toJdbcLiteral(Object value, Dialect dialect, SharedSessionContractImplementor session) { + final TimeZone jdbcTimeZone; + if ( session == null || session.getJdbcTimeZone() == null ) { + jdbcTimeZone = TimeZone.getDefault(); + } + else { + jdbcTimeZone = session.getJdbcTimeZone(); + } // for performance reasons, avoid conversions if we can if ( value instanceof java.util.Date ) { return dialect.formatDateTimeLiteral( (java.util.Date) value, - precision + precision, + jdbcTimeZone ); } else if ( value instanceof java.util.Calendar ) { return dialect.formatDateTimeLiteral( (java.util.Calendar) value, - precision + precision, + jdbcTimeZone ); } else if ( value instanceof TemporalAccessor ) { return dialect.formatDateTimeLiteral( (TemporalAccessor) value, - precision + precision, + jdbcTimeZone ); } @@ -51,19 +62,22 @@ public class JdbcLiteralFormatterTemporal extends BasicJdbcLiteralFormatter { case DATE: { return dialect.formatDateTimeLiteral( unwrap( value, java.sql.Date.class, session ), - precision + precision, + jdbcTimeZone ); } case TIME: { return dialect.formatDateTimeLiteral( unwrap( value, java.sql.Time.class, session ), - precision + precision, + jdbcTimeZone ); } default: { return dialect.formatDateTimeLiteral( unwrap( value, java.util.Date.class, session ), - precision + precision, + jdbcTimeZone ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/CompositeIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/CompositeIdTest.java index c7f6acb4f4..a6b0b86761 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/CompositeIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/CompositeIdTest.java @@ -454,7 +454,6 @@ public class CompositeIdTest { } @Test - @FailureExpected(reason = "Criteria and EmbeddableId as predicate value has not yet been implemented") public void testQueryInAndComposite(SessionFactoryScope scope) { scope.inTransaction( diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/collectionelement/OrderByColumnNameTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/collectionelement/OrderByColumnNameTest.java index 15228c579f..b5b2c671df 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/collectionelement/OrderByColumnNameTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/collectionelement/OrderByColumnNameTest.java @@ -76,7 +76,7 @@ public class OrderByColumnNameTest { ); } - @Entity + @Entity(name = "Product") public static class Product { @Id @GeneratedValue @@ -103,7 +103,7 @@ public class OrderByColumnNameTest { } } - @Entity + @Entity(name = "Widgets") @Inheritance(strategy = InheritanceType.JOINED) public static class Widgets { private String name; @@ -134,13 +134,13 @@ public class OrderByColumnNameTest { } - @Entity - public static class Widget1 extends org.hibernate.orm.test.annotations.collectionelement.Widgets { + @Entity(name = "Widget1") + public static class Widget1 extends Widgets { private String name1; } - @Entity - public static class Widget2 extends org.hibernate.orm.test.annotations.collectionelement.Widgets { + @Entity(name = "Widget2") + public static class Widget2 extends Widgets { private String name2; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/collectionelement/Widgets.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/collectionelement/Widgets.java index 73841ab2ec..e38eb577c1 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/collectionelement/Widgets.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/collectionelement/Widgets.java @@ -41,12 +41,12 @@ public class Widgets { this.id = id; } - @Entity + @Entity(name = "Widget1") public static class Widget1 extends Widgets{ private String name1; } - @Entity + @Entity(name = "Widget2") public static class Widget2 extends Widgets{ private String name2; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/multisession/MultipleSessionCollectionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/multisession/MultipleSessionCollectionTest.java index 9e4da6131c..0e80e1a833 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/multisession/MultipleSessionCollectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/multisession/MultipleSessionCollectionTest.java @@ -32,7 +32,6 @@ import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToMany; -import javax.persistence.Table; import org.hibernate.Hibernate; import org.hibernate.HibernateException; @@ -642,8 +641,7 @@ public class MultipleSessionCollectionTest { ); } - @Entity - @Table(name = "Parent") + @Entity(name = "Parent") public static class Parent { @Id @GeneratedValue @@ -665,8 +663,7 @@ public class MultipleSessionCollectionTest { } - @Entity - @Table(name = "Child") + @Entity(name = "Child") public static class Child { @Id @GeneratedValue diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/columntransformer/Staff.java b/hibernate-core/src/test/java/org/hibernate/orm/test/columntransformer/Staff.java index 26b391064b..dcd6966a1a 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/columntransformer/Staff.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/columntransformer/Staff.java @@ -72,7 +72,7 @@ public class Staff { @Column(name="kooky") @ColumnTransformer( - read = "cast( kooky as VARCHAR )" + read = "cast( kooky as VARCHAR(255) )" ) public String getKooky() { return kooky; } public void setKooky(String kooky) { this.kooky = kooky; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/InheritedEntityGraphTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/InheritedEntityGraphTest.java index 0826413adc..fc089d6aac 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/InheritedEntityGraphTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/InheritedEntityGraphTest.java @@ -221,19 +221,19 @@ public class InheritedEntityGraphTest { } - @Entity + @Entity(name = "Bar") public static class Bar { @Id @GeneratedValue public long id; } - @Entity + @Entity(name = "Foo") public static class Foo extends MappedSupperclass { } - @Entity + @Entity(name = "Foo2") public static class Foo2 { @Id @GeneratedValue public long id; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/filter/DynamicFilterTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/filter/DynamicFilterTest.java index ef4e52ad9b..a3d85f6601 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/filter/DynamicFilterTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/filter/DynamicFilterTest.java @@ -222,7 +222,6 @@ public class DynamicFilterTest extends BaseNonConfigCoreFunctionalTestCase { } @Test - @FailureExpected(jiraKey = "none", message = "not implemented method of QueryParameterBindingsImpl in v6") public void testHqlFilters() { //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // HQL test diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/embeddable/JoinedSubclassWithEmbeddableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/embeddable/JoinedSubclassWithEmbeddableTest.java index 2e75d43731..4de9bc7e76 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/embeddable/JoinedSubclassWithEmbeddableTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/embeddable/JoinedSubclassWithEmbeddableTest.java @@ -107,7 +107,7 @@ public class JoinedSubclassWithEmbeddableTest { } } - @Entity + @Entity(name = "Employee") @Table(name = "employees") public static class Employee extends Person { private Integer employeeNumber; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/embeddable/SingleTableWithEmbeddableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/embeddable/SingleTableWithEmbeddableTest.java index 68babdd776..3ad3fd08fe 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/embeddable/SingleTableWithEmbeddableTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/embeddable/SingleTableWithEmbeddableTest.java @@ -107,7 +107,7 @@ public class SingleTableWithEmbeddableTest { } } - @Entity + @Entity(name = "Employee") @Table(name = "employees") public static class Employee extends Person { private Integer employeeNumber; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/embeddable/TablePerClassWithEmbeddableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/embeddable/TablePerClassWithEmbeddableTest.java index 4283131054..a282bb70d1 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/embeddable/TablePerClassWithEmbeddableTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/embeddable/TablePerClassWithEmbeddableTest.java @@ -107,7 +107,7 @@ public class TablePerClassWithEmbeddableTest { } } - @Entity + @Entity(name = "Employee") @Table(name = "employees") public static class Employee extends Person { private Integer employeeNumber; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/intg/sqm/HqlTranslationNoFactoryTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/intg/sqm/HqlTranslationNoFactoryTests.java index 76b820cddd..789a0ed0d4 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/intg/sqm/HqlTranslationNoFactoryTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/intg/sqm/HqlTranslationNoFactoryTests.java @@ -19,6 +19,7 @@ import org.hibernate.metamodel.model.domain.JpaMetamodel; import org.hibernate.metamodel.model.domain.internal.JpaMetamodelImpl; import org.hibernate.metamodel.MappingMetamodel; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; +import org.hibernate.query.criteria.LiteralHandlingMode; import org.hibernate.query.hql.HqlTranslator; import org.hibernate.query.hql.internal.StandardHqlTranslator; import org.hibernate.query.hql.spi.SqmCreationOptions; @@ -115,6 +116,7 @@ public class HqlTranslationNoFactoryTests { final QueryEngine queryEngine = new QueryEngine( jpaMetamodel, + LiteralHandlingMode.AUTO, // we don't want strict JPA query compliance false, new NamedObjectRepositoryImpl( Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap() ), diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/manytomany/ManyToManyHqlMemberOfQueryTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/manytomany/ManyToManyHqlMemberOfQueryTest.java index 62e39b3f0a..12f8f77a0c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/manytomany/ManyToManyHqlMemberOfQueryTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/manytomany/ManyToManyHqlMemberOfQueryTest.java @@ -25,6 +25,7 @@ import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.MapKeyEnumerated; import javax.persistence.OneToMany; +import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; @@ -283,6 +284,7 @@ public class ManyToManyHqlMemberOfQueryTest { } @Entity(name = "Call") + @Table(name = "phone_call") public static class Call { @Id diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/onetomany/OneToManyHqlMemberOfQueryTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/onetomany/OneToManyHqlMemberOfQueryTest.java index 494efc2115..f35e513948 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/onetomany/OneToManyHqlMemberOfQueryTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/onetomany/OneToManyHqlMemberOfQueryTest.java @@ -26,6 +26,7 @@ import javax.persistence.ManyToOne; import javax.persistence.MapKeyEnumerated; import javax.persistence.OneToMany; import javax.persistence.OrderColumn; +import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; @@ -283,6 +284,7 @@ public class OneToManyHqlMemberOfQueryTest { } @Entity(name = "Call") + @Table(name = "phone_call") public static class Call { @Id diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/OneToOneLazyTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/OneToOneLazyTest.java index 888916944f..75ca4aaf07 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/OneToOneLazyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/OneToOneLazyTest.java @@ -78,7 +78,7 @@ public class OneToOneLazyTest { ); } - @Entity + @Entity(name = "Book") public static class Book { @Id @@ -109,7 +109,7 @@ public class OneToOneLazyTest { } } - @Entity + @Entity(name = "Title") public static class Title { @Id diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/BasicCriteriaExecutionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/BasicCriteriaExecutionTests.java index a1b3a71c05..c4a61fb1ff 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/BasicCriteriaExecutionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/BasicCriteriaExecutionTests.java @@ -10,12 +10,14 @@ import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.ParameterExpression; import javax.persistence.criteria.Root; +import org.hibernate.dialect.H2Dialect; import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.query.criteria.JpaCriteriaQuery; import org.hibernate.query.criteria.JpaRoot; import org.hibernate.testing.orm.domain.gambit.BasicEntity; import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.Test; @@ -106,7 +108,9 @@ public class BasicCriteriaExecutionTests { ); } + // Doing ... where ? = ? ... is only allowed in a few DBs. Since this is useless, we don't bother to emulate this @Test + @RequiresDialect(H2Dialect.class) public void testExecutingBasicCriteriaQueryParameterPredicate(SessionFactoryScope scope) { scope.inStatelessTransaction( session -> { @@ -123,7 +127,9 @@ public class BasicCriteriaExecutionTests { ); } + // Doing ... where ? = ? ... is only allowed in a few DBs. Since this is useless, we don't bother to emulate this @Test + @RequiresDialect(H2Dialect.class) public void testExecutingBasicCriteriaQueryParameterPredicateInStatelessSession(SessionFactoryScope scope) { scope.inStatelessTransaction( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java index 9f87f27f9f..5b44618ae9 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java @@ -8,11 +8,16 @@ package org.hibernate.orm.test.query.hql; import org.hibernate.boot.MetadataSources; import org.hibernate.engine.spi.SessionFactoryImplementor; + +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; @@ -95,9 +100,9 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest { public void testConcatFunctionParameters(SessionFactoryScope scope) { scope.inTransaction( session -> { - assertThat( session.createQuery("select :hello||:world").setParameter("hello","hello").setParameter("world","world").getSingleResult(), is("helloworld") ); - assertThat( session.createQuery("select ?1||?2").setParameter(1,"hello").setParameter(2,"world").getSingleResult(), is("helloworld") ); - assertThat( session.createQuery("select ?1||?1").setParameter(1,"hello").getSingleResult(), is("hellohello") ); + assertThat( session.createQuery("select cast(:hello as String)||cast(:world as String)").setParameter("hello","hello").setParameter("world","world").getSingleResult(), is("helloworld") ); + assertThat( session.createQuery("select cast(?1 as String)||cast(?2 as String)").setParameter(1,"hello").setParameter(2,"world").getSingleResult(), is("helloworld") ); + assertThat( session.createQuery("select cast(?1 as String)||cast(?1 as String)").setParameter(1,"hello").getSingleResult(), is("hellohello") ); } ); } @@ -320,6 +325,7 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest { } @Test + @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsPadWithChar.class) public void testPadFunction(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -955,11 +961,20 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest { } @Test - public void testGroupingFunctions() { + public void testGrouping() { inTransaction( session -> { session.createQuery("select max(e.theDouble), e.gender, e.theInt from EntityOfBasics e group by e.gender, e.theInt") .list(); + } + ); + } + + @Test + @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsGroupByRollup.class) + public void testGroupingFunctions() { + 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)") diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/LiteralTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/LiteralTests.java index a79d699ed3..d057576c94 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/LiteralTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/LiteralTests.java @@ -70,8 +70,8 @@ public class LiteralTests { public void testJdbcTimestampLiteral(SessionFactoryScope scope) { scope.inTransaction( session -> { - session.createQuery( "from EntityOfBasics e1 where e1.theDate = {ts 1999-12-31 12:30:00}" ).list(); - session.createQuery( "from EntityOfBasics e1 where e1.theDate = {ts '1999-12-31 12:30:00'}" ).list(); + session.createQuery( "from EntityOfBasics e1 where e1.theTimestamp = {ts 1999-12-31 12:30:00}" ).list(); + session.createQuery( "from EntityOfBasics e1 where e1.theTimestamp = {ts '1999-12-31 12:30:00'}" ).list(); } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java index cc7278093c..5dacd0f337 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java @@ -35,8 +35,6 @@ import org.hibernate.sql.results.graph.basic.BasicResult; import org.hibernate.sql.results.graph.basic.BasicResultAssembler; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultAssembler; -import org.hibernate.type.CustomType; -import org.hibernate.type.EnumType; import org.hibernate.type.internal.StandardBasicTypeImpl; import org.hibernate.testing.hamcrest.AssignableMatcher; @@ -170,7 +168,7 @@ public class SmokeTests { assertThat( sqlSelection.getJdbcValueExtractor(), notNullValue() ); assertThat( sqlSelection, instanceOf( SqlSelectionImpl.class ) ); - final Expression selectedExpression = ( (SqlSelectionImpl) sqlSelection ).getWrappedSqlExpression(); + final Expression selectedExpression = sqlSelection.getExpression(); assertThat( selectedExpression, instanceOf( ColumnReference.class ) ); final ColumnReference columnReference = (ColumnReference) selectedExpression; assertThat( columnReference.renderSqlFragment( scope.getSessionFactory() ), is( "s1_0.gender" ) ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/SmokeTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/SmokeTests.java index 387d01d305..91e61eb3b2 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/SmokeTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/SmokeTests.java @@ -201,7 +201,7 @@ public class SmokeTests { scope.inTransaction( session -> { final QueryImplementor query = session.createQuery( - "select :param from SimpleEntity e", + "select cast(:param as String) from SimpleEntity e", String.class ); final String attribute1 = query.setParameter( "param", "items" ).uniqueResult(); diff --git a/hibernate-core/src/test/java/org/hibernate/query/sqm/tree/select/HHH13884Test.java b/hibernate-core/src/test/java/org/hibernate/query/sqm/tree/select/HHH13884Test.java index 2b6f698602..681a16ea0e 100644 --- a/hibernate-core/src/test/java/org/hibernate/query/sqm/tree/select/HHH13884Test.java +++ b/hibernate-core/src/test/java/org/hibernate/query/sqm/tree/select/HHH13884Test.java @@ -28,12 +28,10 @@ public class HHH13884Test { @Test public void testDefaultSqmSortSpecificationReverse() { SqmExpression sortExpression = mock( SqmExpression.class ); - String collation = "collation"; - SqmSortSpecification order = new SqmSortSpecification( sortExpression, collation, ASCENDING, FIRST ); + SqmSortSpecification order = new SqmSortSpecification( sortExpression, ASCENDING, FIRST ); assertEquals( sortExpression, order.getSortExpression() ); - assertEquals( collation, order.getCollation() ); assertEquals( ASCENDING, order.getSortOrder() ); assertEquals( FIRST, order.getNullPrecedence() ); diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java index d860f10227..d8e1330c84 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java @@ -6,6 +6,7 @@ */ package org.hibernate.testing.orm.junit; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.Dialect; /** @@ -213,4 +214,16 @@ abstract public class DialectFeatureChecks { return !dialect.supportsNullPrecedence(); } } + + public static class SupportsPadWithChar implements DialectFeatureCheck { + public boolean apply(Dialect dialect) { + return !(dialect instanceof DerbyDialect ); + } + } + + public static class SupportsGroupByRollup implements DialectFeatureCheck { + public boolean apply(Dialect dialect) { + return dialect.supportsGroupByRollup(); + } + } } diff --git a/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle b/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle index 91ba13009b..338c5891ea 100644 --- a/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle +++ b/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle @@ -9,7 +9,7 @@ import org.apache.tools.ant.filters.ReplaceTokens plugins { id 'java-gradle-plugin' - id 'com.github.sebersole.testkit-junit5' version '0.9.5' + id 'com.github.sebersole.testkit-junit5' version '1.0.1' // for portal publishing id "com.gradle.plugin-publish" version "0.12.0"