Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.hibernate.dialect.NullOrdering;
import org.hibernate.dialect.Replacer;
import org.hibernate.dialect.SelectItemReferenceStrategy;
import org.hibernate.dialect.function.InsertSubstringOverlayEmulation;
import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
Expand Down Expand Up @@ -105,6 +106,7 @@

import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.internal.util.JdbcExceptionHelper.extractErrorCode;
import static org.hibernate.query.common.TemporalUnit.DAY;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING;
import static org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers.impliedOrInvariant;
import static org.hibernate.type.SqlTypes.BINARY;
Expand Down Expand Up @@ -374,6 +376,9 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
if ( supportsWindowFunctions() ) {
functionFactory.windowFunctions();
}

functionRegistry.register( "overlay",
new InsertSubstringOverlayEmulation( typeConfiguration, true ) );
}

@Override
Expand Down Expand Up @@ -437,7 +442,7 @@ public String extractPattern(TemporalUnit unit) {
case HOUR -> "to_number(to_char(?2,'%H'))";
case DAY_OF_WEEK -> "(weekday(?2)+1)";
case DAY_OF_MONTH -> "day(?2)";
case EPOCH -> "(to_number(cast(cast(sum(?2-datetime(1970-1-1) year to day) as interval day(9) to day) as varchar(12)))*86400+to_number(cast(cast(sum(cast(?2 as datetime hour to second)-datetime(00:00:00) hour to second) as interval second(6) to second) as varchar(9))))";
case EPOCH -> "(to_number(cast(cast((?2-datetime(1970-1-1) year to day) as interval day(9) to day) as varchar(12)))*86400+to_number(cast(cast((cast(?2 as datetime hour to second)-datetime(00:00:00) hour to second) as interval second(6) to second) as varchar(9))))";
default -> "?1(?2)";
};
}
Expand Down Expand Up @@ -730,15 +735,25 @@ public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalT
return "(?3-?2)";
}
else {
if ( fromTemporalType == TemporalType.DATE && toTemporalType == TemporalType.DATE ) {
// special case: subtraction of two dates results in an integer number of days
return switch ( unit ) {
case NATIVE -> "to_number(cast(?3-?2 as lvarchar))*86400";
case YEAR, MONTH -> "to_number(cast(cast(extend(?3,year to month)-extend(?2,year to month) as interval ?1(9) to ?1) as varchar(12)))";
case DAY -> "to_number(cast(?3-?2 as lvarchar))";
case WEEK -> "floor(to_number(cast(?3-?2 as lvarchar))/7)";
default -> "to_number(cast(?3-?2 as lvarchar))" + DAY.conversionFactor( unit, this );
};
}
return switch ( unit ) {
case NATIVE ->
fromTemporalType == TemporalType.TIME
// arguably, we don't really need to retain the milliseconds for a time, since times don't usually come with millis
? "(mod(to_number(cast(cast(sum(?3-?2) as interval second(6) to second) as varchar(9))),86400)+to_number(cast(cast(sum(?3-?2) as interval fraction to fraction) as varchar(6))))"
: "(to_number(cast(cast(sum(?3-?2) as interval day(9) to day) as varchar(12)))*86400+mod(to_number(cast(cast(sum(?3-?2) as interval second(6) to second) as varchar(9))),86400)+to_number(cast(cast(sum(?3-?2) as interval fraction to fraction) as varchar(6))))";
case SECOND -> "to_number(cast(cast(sum(?3-?2) as interval second(9) to fraction) as varchar(15)))";
case NANOSECOND -> "(to_number(cast(cast(sum(?3-?2) as interval second(9) to fraction) as varchar(15)))*1e9)";
default -> "to_number(cast(cast(sum(?3-?2) as interval ?1(9) to ?1) as varchar(12)))";
? "(mod(to_number(cast(cast(?3-?2 as interval second(6) to second) as varchar(9))),86400)+to_number(cast(cast(?3-?2 as interval fraction to fraction) as varchar(6))))"
: "(to_number(cast(cast(?3-?2 as interval day(9) to day) as varchar(12)))*86400+mod(to_number(cast(cast(?3-?2 as interval second(6) to second) as varchar(9))),86400)+to_number(cast(cast(?3-?2 as interval fraction to fraction) as varchar(6))))";
case SECOND -> "to_number(cast(cast(?3-?2 as interval second(9) to fraction) as varchar(15)))";
case NANOSECOND -> "(to_number(cast(cast(?3-?2 as interval second(9) to fraction) as varchar(15)))*1e9)";
default -> "to_number(cast(cast(?3-?2 as interval ?1(9) to ?1) as varchar(12)))";
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3657,31 +3657,12 @@ public void visitQuerySpec(QuerySpec querySpec) {
this.queryPartForRowNumbering = null;
this.queryPartForRowNumberingClauseDepth = -1;
}
String queryGroupAlias = null;
if ( currentQueryPart instanceof QueryGroup ) {
// We always need query wrapping if we are in a query group and this query spec has a fetch or order by
// clause, because of order by precedence in SQL
if ( querySpec.hasOffsetOrFetchClause() || querySpec.hasSortSpecifications() ) {
queryGroupAlias = "";
// If the parent is a query group with a fetch clause we must use a select wrapper,
// or if the database does not support simple query grouping, we must use a select wrapper
if ( ( !dialect.supportsSimpleQueryGrouping() || currentQueryPart.hasOffsetOrFetchClause() )
// We can skip it though if this query spec is being row numbered,
// because then we already have a wrapper
&& queryPartForRowNumbering != querySpec ) {
queryGroupAlias = " grp_" + queryGroupAliasCounter + '_';
queryGroupAliasCounter++;
appendSql( "select" );
appendSql( queryGroupAlias );
appendSql( ".* from " );
// We need to assign aliases when we render a query spec as subquery to avoid clashing aliases
this.needsSelectAliases = this.needsSelectAliases || hasDuplicateSelectItems( querySpec );
}
else if ( !dialect.supportsDuplicateSelectItemsInQueryGroup() ) {
this.needsSelectAliases = this.needsSelectAliases || hasDuplicateSelectItems( querySpec );
}
}
}
final String queryGroupAlias =
wrapQueryPartsIfNecessary(
querySpec,
currentQueryPart,
queryPartForRowNumbering
);
queryPartStack.push( querySpec );
if ( queryGroupAlias != null ) {
appendSql( OPEN_PARENTHESIS );
Expand Down Expand Up @@ -3710,6 +3691,40 @@ else if ( !dialect.supportsDuplicateSelectItemsInQueryGroup() ) {
}
}

private String wrapQueryPartsIfNecessary(
QuerySpec querySpec, QueryPart currentQueryPart, QueryPart queryPartForRowNumbering) {
// We always need query wrapping if we are in a query group and if this query
// spec has a fetch or order by clause, because of order by precedence in SQL
if ( currentQueryPart instanceof QueryGroup
&& ( querySpec.hasOffsetOrFetchClause() || querySpec.hasSortSpecifications() ) ) {
// If the parent is a query group with a fetch clause, we must use a select wrapper
// Or, if the database does not support simple query grouping, we must use a select wrapper
if ( ( !dialect.supportsSimpleQueryGrouping() || currentQueryPart.hasOffsetOrFetchClause() )
// We can skip it though if this query spec is being row numbered,
// because then we already have a wrapper
&& queryPartForRowNumbering != querySpec ) {
final String queryGroupAlias = " grp_" + queryGroupAliasCounter + '_';
queryGroupAliasCounter++;
appendSql( "select" );
appendSql( queryGroupAlias );
appendSql( ".* from " );
// We need to assign aliases when we render a query spec as subquery to avoid clashing aliases
this.needsSelectAliases = this.needsSelectAliases || hasDuplicateSelectItems( querySpec );
return queryGroupAlias;
}
else if ( !dialect.supportsDuplicateSelectItemsInQueryGroup() ) {
this.needsSelectAliases = this.needsSelectAliases || hasDuplicateSelectItems( querySpec );
return "";
}
else {
return "";
}
}
else {
return null;
}
}

protected void visitQueryClauses(QuerySpec querySpec) {
visitSelectClause( querySpec.getSelectClause() );
visitFromClause( querySpec.getFromClause() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,24 +64,21 @@ public void accept(SqlAstWalker walker) {
}

@Override
public DomainResult createDomainResult(String resultVariable, DomainResultCreationState creationState) {
final SelectClause selectClause = queryPart.getFirstQuerySpec().getSelectClause();
final TypeConfiguration typeConfiguration = creationState.getSqlAstCreationState()
.getCreationContext()
.getMappingMetamodel()
.getTypeConfiguration();
final SqlExpressionResolver sqlExpressionResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver();
if ( selectClause.getSqlSelections().size() == 1 ) {
final SqlSelection first = selectClause.getSqlSelections().get( 0 );
public DomainResult<?> createDomainResult(String resultVariable, DomainResultCreationState creationState) {
final List<SqlSelection> sqlSelections =
queryPart.getFirstQuerySpec().getSelectClause().getSqlSelections();
if ( sqlSelections.size() == 1 ) {
final SqlSelection first = sqlSelections.get( 0 );
final JdbcMapping jdbcMapping = first.getExpressionType().getSingleJdbcMapping();

final SqlSelection sqlSelection = sqlExpressionResolver.resolveSqlSelection(
this,
jdbcMapping.getJdbcJavaType(),
null,
typeConfiguration
);

final SqlSelection sqlSelection =
creationState.getSqlAstCreationState().getSqlExpressionResolver()
.resolveSqlSelection(
this,
jdbcMapping.getJdbcJavaType(),
null,
creationState.getSqlAstCreationState().getCreationContext()
.getTypeConfiguration()
);
return new BasicResult<>(
sqlSelection.getValuesArrayPosition(),
resultVariable,
Expand All @@ -95,20 +92,22 @@ public DomainResult createDomainResult(String resultVariable, DomainResultCreati

@Override
public void applySqlSelections(DomainResultCreationState creationState) {
final SelectClause selectClause = queryPart.getFirstQuerySpec().getSelectClause();
final TypeConfiguration typeConfiguration = creationState.getSqlAstCreationState()
.getCreationContext()
.getMappingMetamodel()
.getTypeConfiguration();
for ( SqlSelection sqlSelection : selectClause.getSqlSelections() ) {
final TypeConfiguration typeConfiguration =
creationState.getSqlAstCreationState().getCreationContext()
.getTypeConfiguration();
final SqlExpressionResolver expressionResolver =
creationState.getSqlAstCreationState().getSqlExpressionResolver();
for ( SqlSelection sqlSelection :
queryPart.getFirstQuerySpec().getSelectClause().getSqlSelections() ) {
sqlSelection.getExpressionType().forEachJdbcType(
(index, jdbcMapping) -> {
creationState.getSqlAstCreationState().getSqlExpressionResolver().resolveSqlSelection(
this,
jdbcMapping.getJdbcJavaType(),
null,
typeConfiguration
);
expressionResolver
.resolveSqlSelection(
this,
jdbcMapping.getJdbcJavaType(),
null,
typeConfiguration
);
}
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.hibernate.annotations.Synchronize;
import org.hibernate.community.dialect.FirebirdDialect;
import org.hibernate.community.dialect.DerbyDialect;
import org.hibernate.community.dialect.InformixDialect;
import org.hibernate.dialect.SybaseASEDialect;

import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
Expand All @@ -39,6 +40,7 @@
@SkipForDialect(dialectClass = DerbyDialect.class, reason = "Derby doesn't support a CONCAT function")
@SkipForDialect(dialectClass = SybaseASEDialect.class, reason = "Sybase doesn't support a CONCAT function")
@SkipForDialect(dialectClass = FirebirdDialect.class, reason = "Firebird doesn't support a CONCAT function")
@SkipForDialect(dialectClass = InformixDialect.class, reason = "Informix doesn't like CONCAT function in GROUP BY")
public class SubselectTest {

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.hibernate.annotations.Generated;
import org.hibernate.cfg.AvailableSettings;

import org.hibernate.community.dialect.InformixDialect;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
Expand Down Expand Up @@ -82,7 +83,7 @@ public void testUpdate(SessionFactoryScope scope) {
entities.forEach( ge -> ge.setName( "updated" ) );

//We need to wait a little to make sure the timestamps produced are different
waitALittle();
waitALittle( scope );
session.flush(); // force update and retrieval of generated values

entities.forEach( ge -> assertThat( ge.getName() ).isEqualTo( "updated" ) );
Expand Down Expand Up @@ -129,9 +130,13 @@ public Instant getUpdateTimestamp() {
}
}

private static void waitALittle() {
private static void waitALittle(SessionFactoryScope scope) {
boolean waitLonger =
// informix clock has low resolution on Mac
scope.getSessionFactory().getJdbcServices().getDialect()
instanceof InformixDialect;
try {
Thread.sleep( 10 );
Thread.sleep( waitLonger ? 1_200 : 10 );
}
catch (InterruptedException e) {
throw new HibernateError( "Unexpected wakeup from test sleep" );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import java.util.List;
import java.util.Locale;

import org.hibernate.HibernateError;
import org.hibernate.annotations.CurrentTimestamp;

import org.hibernate.community.dialect.InformixDialect;
import org.hibernate.testing.jdbc.SQLStatementInspector;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.Jira;
Expand Down Expand Up @@ -77,6 +79,8 @@ public void testUpdate(SessionFactoryScope scope) {
assertThat( pizza.getLastUpdated() ).isEqualTo( updatedTime );
} );

waitALittle( scope );

scope.inTransaction( session -> {
final Pizza pizza = session.find( Pizza.class, 1L );
assertThat( pizza.getToppings() ).hasSize( 3 )
Expand Down Expand Up @@ -177,4 +181,17 @@ public void setPizza(final Pizza pizza) {
}

}

private static void waitALittle(SessionFactoryScope scope) {
boolean waitLonger =
// informix clock has low resolution on Mac
scope.getSessionFactory().getJdbcServices().getDialect()
instanceof InformixDialect;
try {
Thread.sleep( waitLonger ? 1_200 : 2 );
}
catch (InterruptedException e) {
throw new HibernateError( "Unexpected wakeup from test sleep" );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.ArrayList;
import java.util.List;

import org.hibernate.community.dialect.InformixDialect;
import org.hibernate.query.Query;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaCriteriaQuery;
Expand All @@ -17,6 +18,7 @@
import org.hibernate.testing.orm.junit.Jira;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.SkipForDialect;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
Expand All @@ -38,6 +40,8 @@
@DomainModel( annotatedClasses = CriteriaCteOffsetFetchTest.Product.class )
@SessionFactory
@Jira( "https://hibernate.atlassian.net/browse/HHH-17769" )
@SkipForDialect(dialectClass = InformixDialect.class,
reason = "skip with CTEs seems to be broken")
public class CriteriaCteOffsetFetchTest {
@BeforeAll
public void setUp(SessionFactoryScope scope) {
Expand All @@ -52,7 +56,7 @@ public void setUp(SessionFactoryScope scope) {

@AfterAll
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction( session -> session.createMutationQuery( "delete from Product" ).executeUpdate() );
scope.getSessionFactory().getSchemaManager().truncate();
}

@Test
Expand Down
Loading